WebBuilder module examples:

File name: get-new-version.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.send(Wb.submit({ url: 'https://www.geejing.com/get-version', timeout: 8000 }));\n}\nmain();",
    "remark": "Get new version"
  },
  "_icon": "module"
}

File name: about.xwl


{
  "title": "@about",
  "icon": "info",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('wb/modules/sys/portal/home/util.mjs');\n  let lic = Util.getLicensee();\n\n  Wb.set({\n    version: Wb.getConfig('sys.app.version'),\n    type: Wb.getConfig('sys.app.type'),\n    date: Wb.getConfig('sys.app.date').dateText,\n    serverCode: Config.serverCode,\n    lic: (lic ? ('Authorized to ' + lic) : 'Unlicensed')\n  });\n}\nmain();",
    "remark": "View about WebBuilder",
    "obfuscate": "true"
  },
  "_icon": "module",
  "_expanded": true,
  "tags": "",
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "html": "<div cid='lang-en' class='w-html' style='line-height:2;display:none;'>\n  <h2>About WebBuilder</h2>\n  <hr class=\"w-hr\">\n  <table>\n    <tr>\n      <td rowspan=\"6\">\n        <div class=\"w-icon icon-geejing\" style=\"margin:.3em;font-size:3em;\"></div>\n      </td>\n      <td>WebBuilder _$type$_, Date: _$date$_</td>\n    </tr>\n    <tr>\n      <td>Version:\n        _$version$_\n        (<span cid=\"newVersion\"><div class=\"w-icon w-spin icon-loading w-block\"></div> Detecting new version...</span>)\n      </td>\n    </tr>\n    <tr>\n      <td>_$lic$_</td>\n    </tr>\n    <tr>\n      <td>Powerful rapid application development and runtime platform</td>\n    </tr>\n    <tr>\n      <td>Server Code: _$serverCode$_</td>\n    </tr>\n    <tr>\n      <td class='w-padding-tb'>\n        <div cid='buy'></div>\n      </td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <h3>Buy WebBuilder</h3>\n  <p>\n    WebBuilder is released under commercial license, and unauthorized commercial use is illegal.<br>\n    Buy Now: <a href=\"https://www.geejing.com/buy\" target=\"_blank\">https://www.geejing.com/buy</a><br>\n    If you have any question please contact our sales team: <a href=\"mailto:contact@geejing.com\">contact@geejing.com</a>\n  </p>\n  <hr class=\"w-hr\">\n  <h3>Development Team</h3>\n  <div style=\"height:8em;overflow:hidden;\">\n    <div cid=\"team-list\">\n      <p>Amy Liu</p>\n      <p>Andy Williams</p>\n      <p>Bing Xia</p>\n      <p>Brian Galvin</p>\n      <p>Changxing Chen</p>\n      <p>Duncan Soman</p>\n      <p>Eric Kane</p>\n      <p>Fred Gibson</p>\n      <p>Fuming Guo</p>\n      <p>Hongyu Zhang</p>\n      <p>Jiadong Wu</p>\n      <p>Jie Chen</p>\n      <p>Kevin Zhao</p>\n      <p>Lindsey Su</p>\n      <p>Ming Zhao</p>\n      <p>Muran Xiao</p>\n      <p>Olinda Ray</p>\n      <p>Owen Wang</p>\n      <p>Rena Jiang</p>\n      <p>Ruiyang Li</p>\n      <p>Sariya Diamond</p>\n      <p>Terry Sam</p>\n      <p>Tianen Wang</p>\n      <p>Wenhao Jin</p>\n      <p>Wilt Austin</p>\n    </div>\n  </div>\n  <hr class=\"w-hr\">\n  <h3>System Requirements</h3>\n  <table class=\"w-table\">\n    <tr>\n      <th>Items</th>\n      <th>Requirements</th>\n    </tr>\n    <tr>\n      <td>Operating System</td>\n      <td>Windows, Linux, MacOS</td>\n    </tr>\n    <tr>\n      <td>Web Application Servers</td>\n      <td>Any Standard Server(Tomcat, Jetty, Resin, WebLogic etc.)</td>\n    </tr>\n    <tr>\n      <td>Memory</td>\n      <td>800 MB</td>\n    </tr>\n    <tr>\n      <td>Disk Space</td>\n      <td>1 GB(Contains JRE and Tomcat)</td>\n    </tr>\n    <tr>\n      <td>Desktop & Mobile Browser</td>\n      <td>Modern Browsers(Chrome, Edge, Firefox, WebView etc.)</td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <h3>The Third Party Software</h3>\n  <p>WebBuilder requires these third-party software, all of which use friendly licenses and are open source and free.\n  </p>\n  <table class=\"w-table\">\n    <tr>\n      <th>Software Name</th>\n      <th>License</th>\n    </tr>\n    <tr>\n      <td>Apache Softwares</td>\n      <td>Apache</td>\n    </tr>\n    <tr>\n      <td>GraalVM</td>\n      <td>UPL</td>\n    </tr>\n    <tr>\n      <td>JSON.org</td>\n      <td>Public</td>\n    </tr>\n    <tr>\n      <td>SLF4J</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Quartz</td>\n      <td>Apache</td>\n    </tr>\n    <tr>\n      <td>Terser</td>\n      <td>BSD</td>\n    </tr>\n    <tr>\n      <td>Javascript Obfuscator</td>\n      <td>BSD 2-Clause</td>\n    </tr>\n    <tr>\n      <td>CSSO</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Monaco Editor</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>X6</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Quill</td>\n      <td>BSD 3-Clause</td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <div>Warning: WebBuilder has legal copyright registrations in the world. Unauthorized use will result in severe civil\n    and criminal penalties and will be prosecuted to the maximum extent possible under the law.</div>\n  <div>For more information please visit: <a href=\"https://www.geejing.com\" target=\"_blank\">https://www.geejing.com</a>\n  </div>\n  <div>Copyright © Geejing, All Rights Reserved.</div>\n  <div>Contact us: <a href=\"mailto:contact@geejing.com\">contact@geejing.com</a></div>\n</div>\n<div cid='lang-zh-cn' class='w-html' style='line-height:2;display:none;'>\n  <h2>关于 WebBuilder</h2>\n  <hr class=\"w-hr\">\n  <table>\n    <tr>\n      <td rowspan=\"6\">\n        <div class=\"w-icon icon-geejing\" style=\"margin:.3em;font-size:3em;\"></div>\n      </td>\n      <td>WebBuilder _$type$_,发布日期:_$date$_</td>\n    </tr>\n    <tr>\n      <td>\n        版本:_$version$_\n        (<span cid=\"newVersion\"><div class=\"w-icon w-spin icon-loading w-block\"></div> 正在检测新版本...</span>)\n      </td>\n    </tr>\n    <tr>\n      <td>_$lic$_</td>\n    </tr>\n    <tr>\n      <td>强大的快速应用开发和运行平台</td>\n    </tr>\n    <tr>\n      <td>服务器编号:_$serverCode$_</td>\n    </tr>\n    <tr>\n      <td class='w-padding-tb'>\n        <div cid='buy'></div>\n      </td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <h3>购买 WebBuilder</h3>\n  <p>\n    WebBuilder 在商业许可证下发布,未授权的商用是违法的。<br>\n    立即购买:<a href=\"https://www.geejing.com/buy\" target=\"_blank\">https://www.geejing.com/buy</a><br>\n    如果您有任何问题请联系我们的销售团队:<a href=\"mailto:contact@geejing.com\">contact@geejing.com</a>\n  </p>\n  <hr class=\"w-hr\">\n  <h3>开发人员</h3>\n  <div style=\"height:8em;overflow:hidden;\">\n    <div cid=\"team-list\">\n      <p>Amy Liu</p>\n      <p>Andy Williams</p>\n      <p>Bing Xia</p>\n      <p>Brian Galvin</p>\n      <p>Changxing Chen</p>\n      <p>Duncan Soman</p>\n      <p>Eric Kane</p>\n      <p>Fred Gibson</p>\n      <p>Fuming Guo</p>\n      <p>Hongyu Zhang</p>\n      <p>Jiadong Wu</p>\n      <p>Jie Chen</p>\n      <p>Kevin Zhao</p>\n      <p>Lindsey Su</p>\n      <p>Ming Zhao</p>\n      <p>Muran Xiao</p>\n      <p>Olinda Ray</p>\n      <p>Owen Wang</p>\n      <p>Rena Jiang</p>\n      <p>Ruiyang Li</p>\n      <p>Sariya Diamond</p>\n      <p>Terry Sam</p>\n      <p>Tianen Wang</p>\n      <p>Wenhao Jin</p>\n      <p>Wilt Austin</p>\n    </div>\n  </div>\n  <hr class=\"w-hr\">\n  <h3>系统运行要求</h3>\n  <table class=\"w-table\">\n    <tr>\n      <th>条目</th>\n      <th>需求</th>\n    </tr>\n    <tr>\n      <td>操作系统</td>\n      <td>Windows, Linux, MacOS</td>\n    </tr>\n    <tr>\n      <td>Web 应用服务器</td>\n      <td>任何符合工业标准的服务器(Tomcat,Jetty,Resin,WebLogic 等)</td>\n    </tr>\n    <tr>\n      <td>内存</td>\n      <td>800 MB</td>\n    </tr>\n    <tr>\n      <td>磁盘空间</td>\n      <td>1 GB(包含 JRE and Tomcat)</td>\n    </tr>\n    <tr>\n      <td>桌面和移动端浏览器</td>\n      <td>现代浏览器(Chrome, Edge, Firefox, WebView 等)</td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <h3>第三方软件</h3>\n  <p>WebBuilder 是自主研发的软件,完全符合信创要求。运行 WebBuilder 需要这些第三方软件,这些软件都使用了友好的授权并且是开源和免费的。</p>\n  <table class=\"w-table\">\n    <tr>\n      <th>软件名称</th>\n      <th>授权协议</th>\n    </tr>\n    <tr>\n      <td>Apache Softwares</td>\n      <td>Apache</td>\n    </tr>\n    <tr>\n      <td>GraalVM</td>\n      <td>UPL</td>\n    </tr>\n    <tr>\n      <td>JSON.org</td>\n      <td>Public</td>\n    </tr>\n    <tr>\n      <td>SLF4J</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Quartz</td>\n      <td>Apache</td>\n    </tr>\n    <tr>\n      <td>Terser</td>\n      <td>BSD</td>\n    </tr>\n    <tr>\n      <td>Javascript Obfuscator</td>\n      <td>BSD 2-Clause</td>\n    </tr>\n    <tr>\n      <td>CSSO</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Monaco Editor</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>X6</td>\n      <td>MIT</td>\n    </tr>\n    <tr>\n      <td>Quill</td>\n      <td>BSD 3-Clause</td>\n    </tr>\n  </table>\n  <hr class=\"w-hr\">\n  <div>警告:WebBuilder 拥有合法的版权注册,未经授权使用将导致严重的民事后果以及刑事处罚,并将在法律允许的最大范围内受到起诉。</div>\n  <div>更多信息请访问:<a href=\"https://www.geejing.com\" target=\"_blank\">https://www.geejing.com</a>\n  </div>\n  <div>版权所有 © 北京技景科技有限公司,保留所有权利。</div>\n  <div>联系我们:<a href=\"mailto:contact@geejing.com\">contact@geejing.com</a></div>\n</div>",
        "autoScroll": "true"
      },
      "events": {
        "ready": "let el = this.el, mainDiv, teamDiv, updateLink, newVersion;\n\nif (Str.lang == 'zh-cn')\n  mainDiv = el.query('[cid=lang-zh-cn]');\nelse\n  mainDiv = el.query('[cid=lang-en]');\nmainDiv.setStyle('display', 'block');\nteamDiv = mainDiv.query('[cid=team-list]')\nteamDiv.addEventListener('animationend', f => {\n  teamDiv.parentNode.cls = 'w-scroll-true';\n}, {\n  once: true\n});\nteamDiv.className = 'w-vscroll';\nnew Wb.Button({\n  el: mainDiv.query('[cid=buy]'), icon: 'cart', text: Str.buy, type: 'primary', owner: this, handler() {\n    window.open('https://www.geejing.com/buy');\n  }\n});\nnewVersion = mainDiv.query('[cid=newVersion]');\nWb.ajax({\n  url: xpath + '/get-new-version', mask: false, showError: false,\n  failure(resp) {\n    newVersion.textContent = Str.unableDetectNew;\n  },\n  success(resp) {\n    if (parseFloat(resp) != _$version$_) {\n      newVersion.textContent = '';\n      updateLink = newVersion.addTag('a');\n      updateLink.href = 'https://www.geejing.com/download';\n      updateLink.target = '_blank';\n      updateLink.textContent = Str.foundNewVersion.format(resp);\n      updateLink.highlight();\n    } else {\n      newVersion.textContent = Str.alreadyLatestVersion;\n    }\n  }\n});"
      },
      "_expanded": true
    }
  ]
}

File name: set-actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set block actions",
    "serverScript": "let actions = {\n  /**\n   * Get IPs.\n   */\n  getIPs() {\n    var file = new Wb.File(Base.path, 'site/ips.txt'), result;\n    if (!file.exists) {\n      file = new Wb.File(Base.path, 'wb/system/ips.txt');\n    }\n    if (file.exists) {\n      result = file.text;\n    } else {\n      result = '';\n    }\n    Wb.send(result);\n  },\n  /**\n   * Save and reload blocked ips to the cache.\n   */\n  save() {\n    var file = new Wb.File(Base.path, 'site/ips.txt'), text = Wb.payload;\n    if (!file.exists) {\n      file = new Wb.File(Base.path, 'wb/system/ips.txt');\n    }\n    if (text)\n      file.text = text;\n    else if (file.exists)\n      file.remove();\n    this.clear();\n  },\n  /**\n   * Clear and reload blocked ips to the cache.\n   */\n  clear() {\n    Base.reloadBlockedIPs();\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: blocked-ip.xwl


{
  "title": "@blockedIPs",
  "icon": "delete3",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Reload ips.\n   */\n  reload() {\n    Wb.ajax({\n      url: xpath + '/set-actions&xaction=getIPs',\n      success(resp) {\n        app.codeEditor1.value = resp;\n        app.saveItem.disabled = true;\n      }\n    });\n  },\n  /**\n   * Save ips.\n   */\n  save() {\n    Wb.ajax({\n      url: xpath + '/set-actions&xaction=save',\n      data: app.codeEditor1.value,\n      success(resp) {\n        app.saveItem.disabled = true;\n      }\n    });\n  }\n});",
    "beforeunload": "if (!app.saveItem.disabled) {\n  return false;\n}"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "saveItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "saveItem",
                "text": "@Str.save",
                "icon": "save",
                "disabled": "true",
                "keys": "Ctrl+S"
              },
              "events": {
                "click": "app.save();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "refreshItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "refreshItem",
                "text": "@Str.refresh",
                "icon": "refresh"
              },
              "events": {
                "click": "if (app.saveItem.disabled) {\n  app.reload();\n} else {\n  Wb.choose(Str.saveChanges.format('IPs'), btn => {\n    if (btn == 'yes')\n      app.save();\n    else if (btn == 'no') {\n      app.reload();\n    }\n  });\n}"
              }
            },
            {
              "_icon": "item",
              "text": "clearItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "clearItem",
                "text": "@Str.clear",
                "icon": "recycle",
                "tip": "@Str.clearInvalidData"
              },
              "events": {
                "click": "Wb.ajax({\n  url: xpath + '/set-actions&xaction=clear',\n  success() {\n    Wb.tipDone(Str.clear);\n  }\n});"
              }
            }
          ]
        },
        {
          "_icon": "edit",
          "text": "codeEditor1",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "codeEditor1",
            "language": "txt",
            "minimap": "false"
          },
          "events": {
            "ready": "app.reload();",
            "change": "app.saveItem.disabled = false;"
          }
        }
      ]
    }
  ]
}

File name: buy.xwl


{
  "title": "@buyWebBuilder",
  "icon": "cart",
  "img": "",
  "tags": "{url: 'https://www.geejing.com/buy', newWin: true}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Buy WebBuilder"
  },
  "_icon": "module"
}

File name: select-by-date.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select group by date",
    "serverScript": "function main() {\n  let dbName = Wb.getConn().dbName.toLowerCase();\n  if (dbName.includes('sql server'))\n    Wb.sendRowx('select convert(varchar(100), sdate, 23) as date,count(distinct ip_addr) as ct from wb_access group by convert(varchar(100), sdate, 23) order by date desc');\n  else\n    Wb.sendRowx('select date(sdate) as date,count(distinct ip_addr) as ct from wb_access group by date(sdate) order by date(sdate) desc');\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-by-ip.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select group by ip",
    "serverScript": "function main() {\n  Wb.sendRowx('select ip_addr,count(*) as ct from wb_access group by ip_addr order by count(*) desc');\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-by-url.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select group by url",
    "serverScript": "function main() {\n  Wb.sendRowx('select url,count(*) as ct from wb_access group by url order by count(*) desc');\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-down-count.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select download count",
    "serverScript": "function main() {\n  let dbName = Wb.getConn().dbName.toLowerCase();\n  if (dbName.includes('sql server'))\n    Wb.sendRowx(`select convert(varchar(100), sdate, 23) as date,count(distinct ip_addr) as ct from wb_access where url='download' group by convert(varchar(100), sdate, 23) order by date desc`);\n  else\n    Wb.sendRowx(`select date(sdate) as date,count(distinct ip_addr) as ct from wb_access where url='download' group by date(sdate) order by date(sdate) desc`);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select access logs",
    "serverScript": "function main() {\n  let sql, where = [], orderBy;\n\n  if (Params.date1)\n    where.push('sdate>= {?timestamp|date1?}');\n  if (Params.date2)\n    where.push('sdate< {?timestamp|date2?}');\n  where = where.length ? (' where ' + where.join(' and ')) : '';\n  orderBy = Wb.getOrderSql();\n  sql = `select * from wb_access${where} ${orderBy}`;\n  Wb.sendRowx({ sql, rs: 2000 });\n}\nmain();"
  },
  "_icon": "module"
}

File name: access-log.xwl


{
  "title": "访问日志",
  "icon": "data",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Access log"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Create chart option. @priv\n   * @param {Wb.Grid} grid Data source grid.\n   * @param {Wb.Chart} chart Chart component.\n   */\n  createOption(grid, chart) {\n    let xAxis = [], series = [], data;\n\n    grid.each(item => {\n      data = item.data;\n      xAxis.push(data.date);\n      series.push(data.ct);\n    }, true, true);\n    chart.option = {\n      tooltip: {\n        trigger: 'axis',\n        axisPointer: {\n          type: 'shadow'\n        }\n      },\n      xAxis: {\n        type: 'category',\n        data: xAxis\n      },\n      yAxis: {\n        type: 'value'\n      },\n      series: [\n        {\n          data: series,\n          type: 'line',\n          smooth: true\n        }\n      ]\n    };\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1",
        "padding": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "calendar",
              "text": "date1",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date1",
                "placeholder": "@Str.from",
                "width": "9em"
              },
              "_expanded": true
            },
            {
              "_icon": "calendar",
              "text": "date2",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date2",
                "placeholder": "@Str.to",
                "width": "9em"
              },
              "_expanded": true
            },
            {
              "_icon": "item",
              "text": "searchItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "searchItem",
                "tip": "@Str.search",
                "icon": "search"
              },
              "events": {
                "click": "app.listGrid.load({ comps: app.viewport1.tbar });"
              }
            },
            {
              "_icon": "item",
              "text": "resetItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "resetItem",
                "tip": "@Str.reset",
                "icon": "undo"
              },
              "events": {
                "click": "let comps = app.viewport1.tbar;\nWb.reset(comps);\napp.listGrid.load({ comps });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "listGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "listGrid",
            "url": "@xpath + '/select'",
            "sorters": "{name:'sdate',desc:true}",
            "columnsSortable": "true",
            "textSelectable": "true",
            "showMask": "false",
            "maxHeight": "20em",
            "title": "访问日志"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "日期",
                    "fieldName": "sdate",
                    "width": "13em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ipCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ipCol",
                    "text": "IP地址",
                    "fieldName": "ip_addr"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "urlCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "urlCol",
                    "text": "URL地址",
                    "fieldName": "url",
                    "width": "20em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "langCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "langCol",
                    "text": "语言",
                    "fieldName": "lang",
                    "width": "8em"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "panel",
          "text": "panel1",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "panel1",
            "layout": "fit",
            "height": "30em",
            "title": "根据日期统计 IP 统计图"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "chart-bar",
              "text": "dateChart",
              "cls": "Wb.Chart",
              "properties": {
                "cid": "dateChart"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "dateGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "dateGrid",
            "url": "@xpath + '/select-by-date'",
            "title": "根据日期统计 IP 数量",
            "textSelectable": "true",
            "showMask": "false",
            "maxHeight": "20em"
          },
          "_expanded": true,
          "events": {
            "success": "app.createOption(this, app.dateChart);"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "日期",
                    "fieldName": "date",
                    "width": "7em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ctCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ctCol",
                    "text": "IP 数量",
                    "fieldName": "ct",
                    "width": "6em"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "panel",
          "text": "panel2",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "panel2",
            "layout": "fit",
            "height": "30em",
            "title": "根据日期统计下载统计图"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "chart-bar",
              "text": "downloadChart",
              "cls": "Wb.Chart",
              "properties": {
                "cid": "downloadChart"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "downloadGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "downloadGrid",
            "url": "@xpath + '/select-down-count'",
            "title": "根据日期统计下载数量",
            "textSelectable": "true",
            "showMask": "false",
            "maxHeight": "20em"
          },
          "_expanded": true,
          "events": {
            "success": "app.createOption(this, app.downloadChart);"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "日期",
                    "fieldName": "date",
                    "width": "7em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ctCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ctCol",
                    "text": "下载数量",
                    "fieldName": "ct",
                    "width": "6em"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "ipGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "ipGrid",
            "url": "@xpath + '/select-by-ip'",
            "title": "根据 IP 统计次数",
            "textSelectable": "true",
            "showMask": "false",
            "maxHeight": "20em"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ipCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ipCol",
                    "text": "IP 地址",
                    "fieldName": "ip_addr",
                    "width": "10em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ctCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ctCol",
                    "text": "访问次数",
                    "fieldName": "ct",
                    "width": "6em"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "urlGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "urlGrid",
            "url": "@xpath + '/select-by-url'",
            "title": "根据 URL 统计次数",
            "textSelectable": "true",
            "showMask": "false",
            "maxHeight": "20em"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "urlCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "urlCol",
                    "text": "URL 地址",
                    "fieldName": "url",
                    "width": "10em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "ctCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ctCol",
                    "text": "访问次数",
                    "fieldName": "ct",
                    "width": "6em"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add page",
    "serverScript": "function main() {\n  let date = new Date(), rec = { sid: Wb.getId(), sdate: date, last_modified: date };\n\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_cms', insert: Params });\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: buy.xwl


{
  "title": "@buy",
  "icon": "cart",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.invoke('view', { id: 'buy' });",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete pages",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let del = Wb.getObject('del'), rec;\n\n  Wb.sync({ tableName: 'wb_cms', del });\n  if (del.some(item => item.$is_news)) {\n    //Update the latest news modification time to ensure the 304 status code is correct\n    rec = Wb.getRow('select sid from wb_cms where is_news=1 order by last_modified desc');\n    Wb.sync({ tableName: 'wb_cms', update: { $sid: rec.sid, last_modified: new Date() } });\n  }\n  Util.clearBuffer(del);\n}\nmain();"
  },
  "_icon": "module"
}

File name: download.xwl


{
  "title": "@download",
  "icon": "download",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.invoke('view', { id: 'download' });",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit page",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let rec = { last_modified: new Date() };\n\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_cms', update: Params });\n  Util.clearBuffer([Params]);\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-page.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get page content",
    "serverScript": "function main() {\n  Wb.send(Wb.getRecord('select html_content from wb_cms where sid={?sid?}')?.[0] ?? '');\n}\nmain();"
  },
  "_icon": "module"
}

File name: search-page.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Search pages",
    "serverScript": "function main() {\n  let sql;\n\n  sql = 'select distinct title,url from wb_cms';\n  if (Params.query) {\n    Wb.setLike('query');\n    sql += ' where title like {?query?} or url like {?query?}';\n  }\n  sql += ' order by title';\n  Wb.sendRows(sql);\n}\nmain();"
  },
  "_icon": "module"
}

File name: search.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.sendRows({\n  sql: `select title,url,'file' as \"_icon\" from wb_cms where status=1 and lang={?lang?} and url<>'header' and url<>'footer' and url<>'description' and title like {?query?} order by sdate desc`,\n  params: { lang: Params.lang ?? Str.lang, query: '%' + Params.query + '%' }\n});",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select pages",
    "serverScript": "function main() {\n  let sql, where = [];\n\n  sql = `select sid, sdate, url, title, lang, login_required, cached, is_news, is_all, status, '' as html_content, last_modified from wb_cms`;\n  if (Params.searchPageSelect)\n    where.push('(title like {?searchPageSelect?} or url like {?searchPageSelect?})');\n  if (Params.fromDate)\n    where.push('sdate >= {?timestamp|fromDate?}');\n  if (Params.toDate)\n    where.push('sdate <= {?timestamp|toDate?}');\n  if (where.length)\n    sql += ' where ' + where.join(' and ');\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: submit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let now = new Date(), ct;\n\nWb.set({ sid: Wb.getId(), sdate: now, ymd: now.format('yMMdd'), ip_addr: request.getRemoteAddr() });\nct = Wb.getRecord('select count(*) from wb_orders where ymd={?ymd?} and ip_addr={?ip_addr?}')[0];\nif (ct > 200)\n  Wb.raise('Submission exceeds the limit, please contact the administrator.');\nWb.sync({ tableName: 'wb_orders', insert: Params });",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: view.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "const cms = Classes.Cms;\n/**\n * Create page. @priv\n * @param {String} title Page title.\n * @param {String} lang Page language.\n * @param {String} content Page Content.\n * @param {String} [header] Header html.\n * @param {String} [footer] Footer html.\n */\nfunction createPage(title, lang, content, header, footer) {\n  let html, desc;\n\n  desc = getPageData('description', lang).html_content;\n  header ??= getPageData('header', lang);\n  header = header.html_content;\n  footer ??= getPageData('footer', lang);\n  footer = footer.html_content;\n  html = `<!DOCTYPE html>\n<html class=\"w-x w-y w-z\" lang=\"${lang}\">\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, user-scalable=0\"/>\n<meta name=\"description\" content=\"${desc}\"/>\n<title>${title}</title>\n<link rel=\"icon\" href=\"wb/images/app/favicon.ico\"/>\n<link rel=\"stylesheet\" href=\"wb/css/iconfont.css\"/>\n<link rel=\"stylesheet\" href=\"wb/css/classic-wb.css\"/>\n<script src=\"wb/js/locale/wb-${lang}.js\"></script>\n<script src=\"wb/js/wb.js\"></script>\n<script src=\"wb/js/wb-client.js\"></script>\n<script src=\"wb/js/cms.js\"></script>\n</head>\n<body style=\"font-size:14px\" class=\"w-disp-none\">\n<div cid=\"viewport1\" class=\"w-column w-full w-scroll-y w-no-shrink\" id='x-viewport'>\n${header}\n${content}\n${footer}\n</div>\n</body>\n</html>`;\n  return html;\n}\n/**\n * Get news items list.\n * @param {Number} count Items count.\n * @param {String} lang Page language.\n * @param {Boolean} wrapMode Whether auto wrap items.\n * @return {Object} News object with lastModified and content.\n */\nfunction createNews(count, lang, wrapMode) {\n  let i = 0, frs, rs, html, time = 0, summary;\n\n  html = '';\n  frs = Wb.sqlRS({\n    sql: 'select url,title,last_modified,html_content from wb_cms where is_news=1 and lang={?lang?} order by sdate desc',\n    params: { lang }\n  });\n  try {\n    rs = frs.resultSet;\n    while (rs.next()) {\n      i++;\n      if (i > count)\n        break;\n      time = Math.max(time, rs.getTimestamp(3).getTime());\n      html += '<a class=\"' + (wrapMode ? 'w-news-witem' : 'w-news-item') + '\" target=\"_blank\" href=\"view?id=' +\n        rs.getString(1) + '\">';\n      html += '<div class=\"w-row\"><div class=\"w-tt-marker w-margin\"></div><div class=\"w-bold w-ftext\">' +\n        rs.getString(2).substr(0, 80) + '</div></div><div class=\"w-margin-l\">';\n      summary = rs.getString(4).trimLeft();\n      if (summary.startsWith('[')) {\n        summary = summary.substring(1, summary.indexOf(']')).trimRight();\n        html += summary;\n      }\n      html += '</div></a>';\n    }\n  } finally {\n    frs.close();\n  }\n  return { lastModified: time, content: html };\n}\n/**\n * Get page data from buffer or db.\n * @param {String} url Page url.\n * @param {String} lang Page language.\n * @return {Object} Page data.\n */\nfunction getPageData(url, lang) {\n  let key = lang + '$' + url, data;\n\n  data = cms.buffer.get(key);\n  if (!data) {\n    data = Wb.getRow({\n      sql: 'select last_modified,title,html_content,login_required,cached,is_all from wb_cms' +\n        ' where url={?url?} and lang={?lang?} and status=1', params: { url, lang }\n    });\n    if (data?.cached) {\n      let cmsData = new (Java.type('com.wb.tool.CmsData'))();\n      delete data.cached;\n      data.login_required = !!data.login_required;\n      data.is_all = !!data.is_all;\n      Wb.apply(cmsData, data);\n      cms.buffer.put(key, cmsData);\n    }\n  }\n  return data;\n}\n/**\n * Create 404 page. @priv\n */\nfunction create404Page() {\n  let html = `<div class=\"w-center w-flex\">\n<div class=\"w-center w-padding2 w-lh w-gap1\">\n<div class=\"w-icon icon-error w-size3\"></div>\n<div class=\"w-column\">\n<div>HTTP 404</div>\n<div>${Str.e404}</div>\n<a class=\"w-btn w-btn-primary\" style=\"margin-top:1.5em;\" href=\"/\">${Str.home}</a>\n</div>\n</div>\n</div>`;\n  return html;\n}\n/**\n * Get files size object of site folder.\n */\nfunction getFileSizeObject() {\n  let folder = new Wb.File(true, 'site'), result;\n  if (!folder)\n    return null;\n  result = { version: Wb.getConfig('sys.app.version') };\n  folder.each(file => {\n    if (file.isFile)\n      result[file.name] = file.length.mb;\n  });\n  return result;\n}\n/**\n * Record access log. @priv\n */\nfunction recordLog(url, lang) {\n  Wb.sync({ tableName: 'wb_access', insert: { sid: Wb.getId(), sdate: new Date(), ip_addr: request.getRemoteAddr(), url, lang } });\n}\n/**\n * Main function of the module. @priv\n */\nfunction main() {\n  let etag, data, url = Params.id, lang = Params.lang ?? Str.lang, content, isNews, isHome, news;\n\n  url ??= 'home';\n  data = getPageData(url, lang);\n  if (!data && lang != Config.defaultLanguage) {\n    lang = Config.defaultLanguage;\n    data = getPageData(url, lang);\n  }\n  if (data) {\n    if (data.login_required && !Wb.checkLogin())\n      return;\n    recordLog(url, lang);\n    let isAll = data.is_all, lastModified = data.last_modified, header, footer;\n    if (isAll) {\n      lastModified = lastModified.getTime();\n    } else {\n      header = getPageData('header', lang);\n      footer = getPageData('footer', lang);\n      lastModified = Math.max(lastModified, header.last_modified, footer.last_modified);\n    }\n    if (url == 'download') {\n      content = data.html_content;\n      content = content.replaceParams(getFileSizeObject());\n    } else {\n      isHome = url == 'home';\n      isNews = url == 'news';\n      if (isHome || isNews) {\n        news = createNews(isHome ? 6 : 1000, lang, isHome);\n        lastModified = Math.max(news.lastModified, lastModified);\n      }\n      etag = lastModified.toString();\n      if (etag.equals(request.getHeader('If-None-Match'))) {\n        response.setStatus(304);\n        return;\n      }\n      response.setHeader('Etag', etag);\n      content = data.html_content.trimLeft();\n      if (content.startsWith('['))\n        content = content.substr(content.indexOf(']') + 1).trimLeft();\n      if (isHome || isNews)\n        content = content.replace('_$x-news$_', news.content);\n    }\n    content = isAll ? content : createPage(data.title, lang, content, header, footer)\n  } else {\n    content = createPage(Str.e404, lang, create404Page(Str.e404))\n  }\n  WebUtil.send(response, content);\n}\nmain();",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: cms.xwl


{
  "title": "网站内容管理",
  "icon": "container",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/js/monaco.js\"\n]",
    "remark": "Content management system"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Object} - The edit window configs object for page window. */\n  pageWinConfigs: {\n    events: {\n      ready() {\n        let win = this, ct = new Wb.Container({ layout: 'grid5' }), editor = win.lastItem;\n\n        ct.add(win.items.filter(item => item.cid != 'html_content'));\n        ct.each(item => {\n          if (item instanceof Wb.Text)\n            item.gridColumn = 'span 5';\n        });\n        Wb.apply(editor, { text: null });\n        win.add({\n          cname: 'tab', tabPosition: 'bottom', minHeight: '22em', margin: ' 0 1.5em', flex: 1, items: [\n            { title: Str.edit, icon: 'edit', layout: 'fit', closable: null, items: editor },\n            {\n              title: Str.preview, icon: 'preview', closable: null, layout: 'fit', items: { cname: 'viewer', border: 0 }, events: {\n                show() {\n                  this.firstItem.html = editor.value;\n                }\n              }\n            }\n          ]\n        });\n        win.insert(0, ct);\n        win.autoScroll = true;\n        win.layout = 'column';\n      },\n      show() {\n        this.height = this.el.offsetHeight;\n      }\n    }\n  },\n  /**\n   * Load pages data.\n   */\n  loadPages() {\n    app.pageGrid.load({ comps: app.viewport1.tbar });\n  },\n  /**\n   * Method to execute when adding or updating page data fails。\n   * @param {String} resp Response text.\n   */\n  onPageFailure(resp) {\n    Wb.checkExists(resp, 'wb_cms_unq1', app.pageGrid.dictWin.down('url'));\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addPageBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addPageBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "let win = app.pageGrid.dictAdd({\n  url: xpath + '/add',\n  failure: app.onPageFailure\n}, null, null, (grid, row) => {\n  row.data.html_content = null;\n}, app.pageWinConfigs, null, false);\nwin.down('lang').value = Str.lang;"
              }
            },
            {
              "_icon": "item",
              "text": "editPageBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editPageBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let rec = app.pageGrid.selection;\n\nif (rec) {\n  Wb.ajax({\n    url: xpath + '/get-page',\n    params: { sid: rec.data.sid },\n    success(resp) {\n      let win = app.pageGrid.dictEdit({\n        url: xpath + '/edit',\n        failure: app.onPageFailure\n      }, 'title', null, (grid, row) => {\n        row.data.html_content = null;\n      }, app.pageWinConfigs);\n      win.down('html_content').value = resp;\n    }\n  });\n} else {\n  Wb.tipSelect();\n}"
              }
            },
            {
              "_icon": "item",
              "text": "delPageBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delPageBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.pageGrid.removeRecords(xpath + '/del', 'title');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "combo",
              "text": "searchPageSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "searchPageSelect",
                "url": "@xpath + '/search-page'",
                "placeholder": "@Str.title+'/'+Str.url",
                "triggerIcon": "search",
                "clearButton": "true",
                "enterTriggerClick": "true",
                "width": "20em",
                "itemTpl": "<span>{title}</span> <span class=\"w-sub-color\">({url})</span>",
                "textField": "url"
              },
              "events": {
                "action": "app.loadPages();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "label",
              "text": "label1",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "label1",
                "text": "@Str.date"
              }
            },
            {
              "_icon": "calendar",
              "text": "fromDate",
              "cls": "Wb.Date",
              "_expanded": true,
              "properties": {
                "cid": "fromDate",
                "width": "10em"
              }
            },
            {
              "_icon": "label",
              "text": "toLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "toLabel",
                "text": "@Str.to"
              }
            },
            {
              "_icon": "calendar",
              "text": "toDate",
              "cls": "Wb.Date",
              "_expanded": false,
              "properties": {
                "cid": "toDate",
                "beginDate": "fromDate",
                "width": "10em"
              },
              "events": {
                "select": "app.loadPages();"
              }
            },
            {
              "_icon": "item",
              "text": "searchItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "searchItem",
                "icon": "search",
                "text": "@Str.search"
              },
              "events": {
                "click": "app.loadPages();"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "pageGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "pageGrid",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "{name: 'sdate', desc: true}"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editPageBtn.fireEvent('click');",
            "beforeloadcolumns": "let col = columns.find(col => col.fieldName == 'title');\n\nif (col)\n  col.width = -1;"
          }
        }
      ]
    }
  ]
}

File name: create.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Create ip list",
    "serverScript": "function main() {\n  let condi = '', myIp = request.getRemoteAddr(), ipList;\n\n  ipList = new Wb.File(true, 'site/fixed-ips.txt').text.split('\\n');\n  condi = `sdate > '` + (Params.begin || '2025-9-18') + `'`;\n  if (Params.end)\n    condi += ` and sdate < '` + Params.end + `'`;\n  Wb.getAllRows({\n    sql: `select distinct ip_addr from wb_access where ip_addr not in \n      (select distinct ip_addr from wb_access where url<>'home') and ` + condi, fn(row) {\n      if (row.ip_addr != myIp)\n        ipList.push(row.ip_addr);\n    }\n  });\n  new Wb.File(true, 'site/ips.txt').text = ipList.unique().join('\\n');\n  Base.reloadBlockedIPs();\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-ips.xwl


{
  "title": "获得 IP",
  "icon": "script",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "center"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "panel",
          "text": "panel1",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "panel1",
            "layout": "grid1"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "calendar",
              "text": "begin",
              "cls": "Wb.Date",
              "properties": {
                "cid": "begin",
                "text": "开始时间",
                "required": "true",
                "value": "@new Date().addDay(-7)"
              }
            },
            {
              "_icon": "calendar",
              "text": "end",
              "cls": "Wb.Date",
              "properties": {
                "cid": "end",
                "text": "结束时间"
              }
            },
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "生成",
                "type": "primary"
              },
              "events": {
                "click": "if (Wb.verify(app.panel1)) {\n  Wb.ajax({\n    url: xpath + '/create',\n    comps: app.panel1,\n    success(resp) {\n      Wb.tipSucc();\n    }\n  });\n}"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: create-license.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Create license file",
    "serverScript": "function main() {\n  let text, now = new Date(), years = Wb.getInt('years'), yearsText, users = Wb.getInt('user_count'), usersText,\n    server = Params.serverCode, serverText, licenseText, licensee = Params.licensee;\n\n  if (years == 6) {\n    yearsText = '(No limit)';\n    years = 0;\n  } else {\n    years = now.addDay(7).addYear(years).datePart;\n    yearsText = years.format('y-MM-dd');\n  }\n  if (users == 1000)\n    usersText = '(No limit)';\n  else\n    usersText = users;\n  if (server == '$$$')\n    serverText = '(Generic)';\n  else\n    serverText = server;\n  licenseText = 'Licensee: ' + licensee +\n    '\\nExpire date: ' + yearsText + '\\nUsers: ' + usersText + '\\nServer Code: ' + serverText;\n  text = '--- Warning: Do not modify this file --- \\n' + licenseText +\n    '\\nSignature: ' + Encrypter.encrypt(Wb.encode({ licensee, server, users, years, sn: Params.sid }), '@#7D1d9!');\n  let remark, lock = new Wb.Lock('wb.tables.order');\n  try {\n    remark = Wb.getRecord('select remark from wb_orders where sid={?sid?}')[0];\n    if (remark)\n      remark += '\\n';\n    else\n      remark = '';\n    remark += now.dateTimeText + ': create license (\\n' + licenseText + '\\n)';\n    Wb.set({ remark });\n    Wb.sql('update wb_orders set remark={?clob|remark?} where sid={?sid?}');\n  } finally {\n    lock.unlock();\n  }\n  Wb.send({ text, remark });\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete orders",
    "serverScript": "function main() {\n  let del = Wb.getObject('del');\n\n  Wb.sync({ tableName: 'wb_orders', del });\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit order",
    "serverScript": "function main() {\n  Wb.sync({ tableName: 'wb_orders', update: Params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select orders",
    "serverScript": "function main() {\n  let sql, dbName = Wb.getConn().dbName.toLowerCase(), where, orderBy;\n\n  if (Params.search) {\n    Wb.setLike('search');\n    where = ' where payer like {?search?}'\n  } else {\n    where = '';\n  }\n  orderBy = Wb.getOrderSql();\n  if (dbName.includes('sql server')) {\n    sql = `select * from (select row_number() over(${orderBy}) as rownumber, * from \n      wb_orders${where}) a\n      where rownumber > {?bigint|_from?} and rownumber < {?bigint|_to?}+2 ${orderBy}\n      `;\n    Wb.sendRowx({ sql, paging: false, total: `select count(*) from wb_orders${where}` });\n  } else {\n    Wb.sendRowx('select * from wb_orders' + where + orderBy);\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: order.xwl


{
  "title": "订单列表",
  "icon": "order",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Order list"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "editWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "editWin",
        "layout": "form1",
        "resetDialog": "true",
        "icon": "edit",
        "title": "修改订单",
        "width": "50em"
      },
      "items": [
        {
          "_icon": "number-edit",
          "text": "total_price",
          "cls": "Wb.Number",
          "properties": {
            "cid": "total_price",
            "text": "实付总价"
          },
          "_expanded": true
        },
        {
          "_icon": "switcher-on",
          "text": "usd",
          "cls": "Wb.Toggle",
          "properties": {
            "cid": "usd",
            "text": "美元"
          },
          "_expanded": true
        },
        {
          "_icon": "line",
          "text": "line1",
          "cls": "Wb.Line",
          "properties": {
            "cid": "line1",
            "title": "可选项"
          }
        },
        {
          "_icon": "text",
          "text": "payer",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "payer",
            "text": "付款人"
          }
        },
        {
          "_icon": "text",
          "text": "licensee",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "licensee",
            "text": "被授权人"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "text",
          "text": "contact",
          "cls": "Wb.Text",
          "properties": {
            "cid": "contact",
            "text": "联系人"
          }
        },
        {
          "_icon": "text",
          "text": "email",
          "cls": "Wb.Text",
          "properties": {
            "cid": "email",
            "text": "电子邮件"
          }
        },
        {
          "_icon": "text",
          "text": "phone",
          "cls": "Wb.Text",
          "_expanded": false,
          "properties": {
            "cid": "phone",
            "text": "联系电话"
          }
        },
        {
          "_icon": "switcher-on",
          "text": "online_support",
          "cls": "Wb.Toggle",
          "properties": {
            "cid": "online_support",
            "text": "技术支持"
          }
        },
        {
          "_icon": "number-edit",
          "text": "user_count",
          "cls": "Wb.Number",
          "_expanded": true,
          "properties": {
            "cid": "user_count",
            "text": "用户数",
            "minValue": "10",
            "maxValue": "1000",
            "decimalCount": "0",
            "upDownKey": "true"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "number-edit",
          "text": "years",
          "cls": "Wb.Number",
          "properties": {
            "cid": "years",
            "text": "使用年限",
            "maxValue": "6",
            "minValue": "1",
            "decimalCount": "0",
            "upDownKey": "true"
          },
          "_expanded": true,
          "hasDupCid": 1
        },
        {
          "_icon": "number-edit",
          "text": "total_count",
          "cls": "Wb.Number",
          "properties": {
            "cid": "total_count",
            "text": "授权数量"
          }
        },
        {
          "_icon": "textarea",
          "text": "remark",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "remark",
            "text": "备注",
            "flex": "1",
            "minHeight": "15em"
          },
          "_expanded": true
        }
      ]
    },
    {
      "_icon": "window",
      "text": "licenseWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "licenseWin",
        "icon": "card",
        "layout": "grid1",
        "resetDialog": "true",
        "title": "下载授权证书"
      },
      "events": {
        "ok": "let win = this, row = app.selectRow, data = row.data;\nif (Wb.verify(win)) {\n  Wb.ajax({\n    url: xpath + '/create-license',\n    comps: win,\n    params: data,\n    json: true,\n    success(resp) {\n      row.set('remark', resp.remark);\n      Wb.downloadText(resp.text, 'license.txt');\n      win.close();\n    }\n  });\n}"
      },
      "items": [
        {
          "_icon": "trigger",
          "text": "serverCode",
          "cls": "Wb.Trigger",
          "properties": {
            "cid": "serverCode",
            "text": "服务器编号",
            "onTriggerClick": "this.value = '$$$';",
            "triggerIcon": "card",
            "trigger": "{tip: '不限制服务器'}",
            "required": "true"
          }
        },
        {
          "_icon": "text",
          "text": "licensee",
          "cls": "Wb.Text",
          "properties": {
            "cid": "licensee",
            "text": "被授权人",
            "readonly": "true"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "number-edit",
          "text": "user_count",
          "cls": "Wb.Number",
          "_expanded": true,
          "properties": {
            "cid": "user_count",
            "text": "用户数",
            "minValue": "10",
            "maxValue": "1000",
            "decimalCount": "0",
            "upDownKey": "true",
            "readonly": "true"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "number-edit",
          "text": "years",
          "cls": "Wb.Number",
          "properties": {
            "cid": "years",
            "text": "使用年限",
            "maxValue": "6",
            "minValue": "1",
            "decimalCount": "0",
            "upDownKey": "true",
            "readonly": "true"
          },
          "_expanded": true,
          "hasDupCid": 1
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "columnsSortable": "true",
            "sorters": "{name: 'sdate', desc: true}",
            "stateId": "wb.cms.order",
            "multiSelect": "true"
          },
          "events": {
            "itemdblclick": "app.editItem.fireClick('click');"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "editItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "editItem",
                    "icon": "edit",
                    "text": "编辑",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "let rec = app.grid1.selection;\napp.grid1.editRecord(xpath + '/edit', app.editWin, 'payer');"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delItem",
                    "icon": "delete",
                    "text": "删除",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "app.grid1.removeRecords(xpath + '/del', 'payer');"
                  }
                },
                {
                  "_icon": "item",
                  "text": "licenseItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "licenseItem",
                    "icon": "card",
                    "text": "证书",
                    "keys": "Ctrl+M"
                  },
                  "events": {
                    "click": "let row = app.grid1.selection, win = app.licenseWin;\n\nif (!row) {\n  Wb.tipSelect();\n  return;\n}\napp.selectRow = row;\nWb.setValue(win, row.data);\nwin.show();"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "text",
                  "text": "search",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "search",
                    "clearButton": "true",
                    "placeholder": "@Str.search",
                    "flex": "1",
                    "minWidth": "5em",
                    "maxWidth": "20em"
                  },
                  "events": {
                    "change": "app.grid1.delayLoad({ comps: app.search });"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "fieldName": "sdate",
                    "text": "日期",
                    "width": "14em",
                    "render": "let wrapEl = el.addEl('w-row w-align-center');\nel.removeCls('w-text');\nwrapEl.addEl('w-item-icon w-icon icon-' + (data.total_price == null ? 'help1' : 'ok'));\nwrapEl.addEl('w-text').textContent = value.dateTimeText;"
                  }
                },
                {
                  "_icon": "column",
                  "text": "idCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "idCol",
                    "fieldName": "sid",
                    "text": "序列号",
                    "width": "11em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "payerCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "payerCol",
                    "fieldName": "payer",
                    "text": "付款人",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "licenseeCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "licenseeCol",
                    "fieldName": "licensee",
                    "text": "被授权人",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "usdCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "usdCol",
                    "fieldName": "usd",
                    "text": "美元",
                    "width": "4em",
                    "checkBox": "roTrue"
                  }
                },
                {
                  "_icon": "column",
                  "text": "priceCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "priceCol",
                    "fieldName": "total_price",
                    "text": "实付总价",
                    "type": "floatText",
                    "width": "7em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "price1Col",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "price1Col",
                    "text": "应付总价",
                    "width": "14em",
                    "sortable": "false",
                    "render": "let user = data.user_count, year = 1, price;\n\nprice = Math.round((10500 + (179 - (user / 20)) * (user - 10)) * year * (10 - year / 1.5) / 10);\nprice *= data.total_count;\nif (data.online_support)\n  price += (24000 + user * 40);\nreturn '¥' + price.intText + ' / $' + Math.round(price / 6).intText;"
                  }
                },
                {
                  "_icon": "column",
                  "text": "contactCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "contactCol",
                    "fieldName": "contact",
                    "text": "联系人",
                    "width": "7em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "emailCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "emailCol",
                    "fieldName": "email",
                    "text": "电子邮件",
                    "width": "13em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "phoneCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "phoneCol",
                    "fieldName": "phone",
                    "text": "联系电话"
                  }
                },
                {
                  "_icon": "column",
                  "text": "supportCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "supportCol",
                    "fieldName": "online_support",
                    "text": "技术支持",
                    "width": "6em",
                    "checkBox": "roTrue"
                  }
                },
                {
                  "_icon": "column",
                  "text": "usersCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "usersCol",
                    "fieldName": "user_count",
                    "text": "用户数量",
                    "width": "6em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "yearsCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "yearsCol",
                    "fieldName": "years",
                    "text": "使用年限",
                    "width": "6em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "countCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "countCol",
                    "fieldName": "total_count",
                    "text": "授权数量",
                    "width": "6em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "remarkCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "remarkCol",
                    "fieldName": "remark",
                    "text": "备注",
                    "width": "20em",
                    "sortable": "false"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: select-ip-count.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select ip count list",
    "serverScript": "function main() {\n  let dbName = Wb.getConn().dbName.toLowerCase();\n  if (dbName.includes('sql server'))\n    Wb.sendRowx('select convert(varchar(100), log_date, 23) as date,count(distinct ip_addr) as ct from wb_update group by convert(varchar(100), log_date, 23) order by date desc');\n  else\n    Wb.sendRowx('select date(log_date) as date,count(distinct ip_addr) as ct from wb_update group by date(log_date) order by date(log_date) desc');\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select run logs",
    "serverScript": "function main() {\n  Wb.sendRowx({ sql: 'select * from wb_update ' + Wb.getOrderSql(), rs: 2000 });\n}\nmain();"
  },
  "_icon": "module"
}

File name: run-log.xwl


{
  "title": "运行日志",
  "icon": "play",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Run log"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1",
        "padding": "true"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "grid",
          "text": "listGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "listGrid",
            "title": "运行日志",
            "icon": "log",
            "url": "@xpath + '/select'",
            "maxHeight": "20em",
            "columnsSortable": "true",
            "sorters": "{name:'log_date',desc:true}",
            "textSelectable": "true"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "logDateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "logDateCol",
                    "text": "日期",
                    "fieldName": "log_date",
                    "width": "12em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "ipAddrCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ipAddrCol",
                    "text": "IP 地址",
                    "fieldName": "ip_addr",
                    "width": "10em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "userNameCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "userNameCol",
                    "text": "用户名称",
                    "fieldName": "user_name"
                  }
                },
                {
                  "_icon": "column",
                  "text": "macCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "macCol",
                    "text": "机器码",
                    "fieldName": "mac"
                  }
                },
                {
                  "_icon": "column",
                  "text": "computerNameCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "computerNameCol",
                    "text": "计算机名",
                    "fieldName": "computer_name"
                  }
                },
                {
                  "_icon": "column",
                  "text": "userDomainCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "userDomainCol",
                    "text": "用户域",
                    "fieldName": "user_domain"
                  }
                },
                {
                  "_icon": "column",
                  "text": "osNameCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "osNameCol",
                    "text": "操作系统",
                    "fieldName": "os_name"
                  }
                },
                {
                  "_icon": "column",
                  "text": "appTitleCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "appTitleCol",
                    "text": "应用标题",
                    "fieldName": "app_title",
                    "width": "13em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "licenseeCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "licenseeCol",
                    "text": "被授权人",
                    "fieldName": "licensee",
                    "width": "13em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "snCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "snCol",
                    "text": "序列号",
                    "fieldName": "sn"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "panel",
          "text": "chartPanel",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "chartPanel",
            "title": "IP统计图",
            "layout": "fit",
            "height": "30em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "chart-bar",
              "text": "ipChart",
              "cls": "Wb.Chart",
              "properties": {
                "cid": "ipChart"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "ipTotalGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "ipTotalGrid",
            "title": "按IP统计",
            "icon": "total",
            "maxHeight": "20em",
            "url": "@xpath + '/select-ip-count'",
            "pageSize": "1000",
            "textSelectable": "true"
          },
          "events": {
            "success": "let xAxis = [], series = [], data;\n\nthis.each(item => {\n  data = item.data;\n  xAxis.push(data.date);\n  series.push(data.ct);\n}, true, true);\napp.ipChart.option = {\n  tooltip: {\n    trigger: 'axis',\n    axisPointer: {\n      type: 'shadow'\n    }\n  },\n  xAxis: {\n    type: 'category',\n    data: xAxis\n  },\n  yAxis: {\n    type: 'value'\n  },\n  series: [\n    {\n      data: series,\n      type: 'line',\n      smooth: true\n    }\n  ]\n};"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "日期",
                    "fieldName": "date",
                    "width": "12em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "ctCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ctCol",
                    "text": "数量",
                    "fieldName": "ct"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: webbuilder.xwl


{
  "title": "WebBuilder Examples",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let html;\n\nhtml = '<p>WebBuilder module examples:</p>\\n'\nWb.cascade(Wb.File.moduleFolder, file => {\n  if (file.isModuleFile && !file.isMinFile) {\n    html += '<p>File name: ' + file.name + '</p>\\n';\n    html += '<pre style=\"background:#eee\"><code>\\n' + file.text.html + '\\n</code></pre>\\n';\n  }\n});\nWb.send(html);",
    "remark": "WebBuilder examples",
    "loginRequired": "false"
  },
  "_icon": "module",
  "items": []
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add config item",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let { nameEditor, typeEditor, valueEditor, listEditor, descEditor } = Params;\n  Config.set(nameEditor, valueEditor, typeEditor, listEditor, descEditor);\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete config items",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  Config.remove(Params.nameEditor);\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit config item",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let { nameEditor, valueEditor } = Params;\n  Wb.setConfig(nameEditor, valueEditor);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-list.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select config items",
    "serverScript": "function main() {\n  let items = [], path = Params.path + '.', pos = path.length, name, value, type,\n    object = new Wb.File(true, 'wb/system/config.json').object;\n\n  Wb.each(object, (k, v) => {\n    if (k.startsWith(path)) {\n      name = k.substr(pos);\n      if (!name.includes('.')) {\n        v = object[k];\n        value = v.value;\n        type = v.type;\n        if (type == 'object' || type == 'array')\n          value = Wb.encode(value);\n        items.push({\n          name, value, type, list: Wb.encode(v.list),\n          desc: v?.[Str.lang] ?? v?.text\n        });\n      }\n    }\n  });\n  items.mixSort('name');\n  Wb.send(items);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-tree.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select config folder items",
    "serverScript": "function main() {\n  let items = [], buffer = Config.getBuffer();\n  buffer.forEach(k => items.push(k.beforeItem('.')));\n  items = Wb.Builder.getTree(items, '.');\n  items.find(item => item.text == 'sys')._expanded = true;\n  Wb.send(items);\n}\nmain();"
  },
  "_icon": "module"
}

File name: config.xwl


{
  "title": "@systemConfig",
  "icon": "gear",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "System configuration tool"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "/**\n * System configuration management tools.\n */\nWb.define(app, {\n  /** @property {String} - The current config item path. */\n  get$path() {\n    return app.tree1.getPath('text', '.');\n  },\n  /**\n   * Fix the value. @priv\n   * @param {Object} value Value to be fixed.\n   * @param {String} type Value type.\n   * @return {Object} The fixed value.\n   */\n  fixValue(value, type) {\n    if (type == 'object' || type == 'array')\n      return Wb.encode(Wb.parse(value));\n    else if (type == 'date')\n      return value.textValue;\n    else\n      return value;\n  },\n  /**\n   * Verify the validity of the input value. @priv\n   * @return {Boolean} Return true if the value is valid.\n   */\n  verifyValue() {\n    let type = app.typeEditor.value, value = app.valueEditor.value, isArray, valid;\n\n    isArray = type == 'array';\n    if (isArray || type == 'object') {\n      try {\n        valid = isArray ? Wb.isArray(Wb.parse(value)) : Wb.isObject(Wb.parse(value));\n      } catch (e) {\n        valid = false;\n      }\n      if (!valid) {\n        Wb.warn(Str.invalidValue.format(value), f => app.valueEditor.focus());\n        return false;\n      }\n    }\n    return true;\n  },\n  /**\n   * Creates a value editor of the specified type. @priv\n   * @param {String} type The value type.\n   * @param {String} [data] List data, only applicable when the type is enum.\n   * @return {Wb.Control} The created editor.\n   */\n  createValueEditor(type, data) {\n    let editor;\n    switch (type) {\n      case 'string':\n        editor = { cname: 'text' };\n        break;\n      case 'int':\n      case 'long':\n        editor = { cname: 'number', decimalCount: 0 };\n        break;\n      case 'double':\n        editor = { cname: 'number' };\n        break;\n      case 'date':\n        editor = { cname: 'datetime' };\n        break;\n      case 'bool':\n        editor = { cname: 'toggle' };\n        break;\n      case 'enum':\n        if (data)\n          editor = { cname: 'select', forceSelect: true, data };\n        else\n          editor = { cname: 'text' };\n        break;\n      default:\n        editor = { cname: 'textArea', height: '6em' };\n    }\n    if (type != 'string')\n      editor.required = true;\n    Wb.apply(editor, { text: Str.value, cid: 'valueEditor', app });\n    return Wb.create(editor);\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "addWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "addWin",
        "dialog": "true",
        "layout": "grid1",
        "width": "60em",
        "title": "@Str.add",
        "icon": "add"
      },
      "events": {
        "ok": "let win = this, params = Wb.getValue(win), name = params.nameEditor, node = app.tree1, path,\n  newNode, valid, listValue = params.listEditor, type = params.typeEditor, fixedList;\n\nif (listValue) {\n  try {\n    valid = Wb.isArray(Wb.parse(listValue));\n  } catch (e) {\n    valid = false;\n  }\n  if (!valid) {\n    Wb.warn(Str.invalidValue.format(listValue), f => app.listEditor.focus());\n    return;\n  }\n}\nif (!app.verifyValue())\n  return;\nfixedList = Wb.encode(Wb.parse(listValue));\nparams.listEditor = fixedList;\nif (name.startsWith('.'))\n  name = app.path + name;\nelse if (!name.includes('.'))\n  name = app.path + '.' + name;\nparams.nameEditor = name;\nWb.ajax({\n  url: xpath + '/add',\n  params,\n  success() {\n    path = name.beforeItem('.');\n    name = name.lastItem('.');\n    path.split('.').forEach(item => {\n      node = node.find(sub => sub.text == item) ?? node.addData({ text: item, items: [] });\n    });\n    if (node.selected) {\n      let desc = params.descEditor, trimDesc = desc.trim();\n\n      //allow to set multi-lan desc\n      if (trimDesc.startsWith('{') && trimDesc.endsWith('}')) {\n        try {\n          trimDesc = Wb.parse(trimDesc);\n          desc = trimDesc[Str.lang] ?? trimDesc.text;\n        } catch (e) {\n          //ignore\n        }\n      }\n      app.grid1.addData({\n        name, value: app.fixValue(params.valueEditor, type), type, desc, list: fixedList\n      }).select();\n    } else {\n      app.selConfigName = name;\n      node.select();\n    }\n    win.hide();\n  }\n});"
      },
      "items": [
        {
          "_icon": "text",
          "text": "nameEditor",
          "cls": "Wb.Text",
          "properties": {
            "cid": "nameEditor",
            "text": "@Str.name",
            "required": "true"
          }
        },
        {
          "_icon": "combo",
          "text": "typeEditor",
          "cls": "Wb.Select",
          "_expanded": false,
          "properties": {
            "cid": "typeEditor",
            "text": "@Str.type",
            "data": "['string', 'int', 'long', 'double', 'date', 'bool', 'enum', 'array', 'object', 'text']",
            "editable": "false",
            "valueIndex": "0",
            "required": "true"
          },
          "events": {
            "select": "let addWin = app.addWin, type = data.text;\naddWin.down('valueEditor')?.destroy();\naddWin.insertAfter(app.createValueEditor(type), this);\nif (type == 'enum') {\n  if (!app.listEditor)\n    addWin.insertAfter({ cname: 'text', text: Str.list, cid: 'listEditor', required: true, app },\n      app.valueEditor);\n} else {\n  app.listEditor?.destroy();\n}\naddWin.show();"
          }
        },
        {
          "_icon": "text",
          "text": "valueEditor",
          "cls": "Wb.Text",
          "_expanded": false,
          "properties": {
            "cid": "valueEditor",
            "text": "@Str.value"
          }
        },
        {
          "_icon": "text",
          "text": "descEditor",
          "cls": "Wb.Text",
          "properties": {
            "cid": "descEditor",
            "text": "@Str.desc",
            "required": "true"
          }
        }
      ]
    },
    {
      "_icon": "window",
      "text": "editWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "editWin",
        "dialog": "true",
        "layout": "grid1",
        "width": "60em",
        "title": "@Str.edit",
        "icon": "edit"
      },
      "events": {
        "ok": "let win = this, params = Wb.getValue(win), rec = app.grid1.selection;\n\nif (!app.verifyValue())\n  return;\nparams.nameEditor = app.path + '.' + rec.data.name;\nWb.ajax({\n  url: xpath + '/edit',\n  params,\n  success() {\n    let value;\n\n    try {\n      value = app.fixValue(params.valueEditor, rec.data.type)\n    } catch (e) {\n      Wb.error(Str.invalidExpress);\n      return;\n    }\n    rec.set('value', value);\n    win.hide();\n  }\n});"
      }
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "let addWin = app.addWin;\n\nWb.reset(Wb.pluck(addWin.children, ['nameEditor', 'valueEditor', 'listEditor', 'descEditor']));\naddWin.show();"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let data = app.grid1.selection?.data;\nif (!data) {\n  Wb.tipSelect();\n  return;\n}\nlet editWin = app.editWin, children = editWin.children, list = data.list;\nchildren.valueEditor?.destroy();\neditWin.add(app.createValueEditor(data.type, list ? Wb.decode(list) : null));\neditWin.subTitle = data.name;\nchildren.valueEditor.value = data.value;\neditWin.show();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "let rec = app.grid1.selection;\nif (!rec) {\n  Wb.tipSelect();\n  return;\n}\nWb.confirm(Wb.getActionHint(rec, 'name'), f => {\n  Wb.ajax({\n    url: xpath + '/del',\n    params: { nameEditor: app.path + '.' + rec.data.name },\n    success() {\n      app.grid1.delRecords();\n    }\n  });\n});"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "refreshBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "refreshBtn",
                "text": "@Str.refresh",
                "icon": "refresh"
              },
              "events": {
                "click": "app.tree1.loadSelect();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "label",
              "text": "pathLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "pathLabel",
                "textSelectable": "true"
              }
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "tree1",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "tree1",
            "width": "17em",
            "url": "@xpath + '/select-tree'",
            "autoSelect": "true"
          },
          "events": {
            "selectionchange": "let path = app.path;\napp.grid1.load({ params: { path } });\napp.pathLabel.text = path;"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          },
          "_expanded": true
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "flex": "1",
            "pagingBar": "false",
            "url": "@xpath + '/select-list'",
            "autoLoad": "false"
          },
          "_expanded": true,
          "events": {
            "selectionchange": "let item = this.selection, node = app.tree1.selection;\napp.pathLabel.text = node.getPath('text', '.') + (item ? ('.' + item.data.name) : '');",
            "success": "/** @property {String} selConfigName The name of the configuration item selected after the table is loaded successfully. */\nif (app.selConfigName) {\n  this.find(item => item.data.name == app.selConfigName)?.select();\n  app.selConfigName = null;\n}",
            "itemdblclick": "app.editBtn.fireEvent('click');"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "fieldName": "name",
                    "text": "@Str.name",
                    "width": "14em"
                  },
                  "text": "nameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "valueCol",
                    "fieldName": "value",
                    "text": "@Str.value",
                    "width": "23em",
                    "align": "left",
                    "render": "if (data.type == 'date')\n  return value.dateValue.dateTimeText;\nelse\n  return value;"
                  },
                  "text": "valueCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "descCol",
                    "fieldName": "desc",
                    "text": "@Str.desc",
                    "width": "-1"
                  },
                  "text": "descCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: config.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit database source",
    "serverScript": "function main() {\n  let file = new Wb.File(true, 'wb/system/db/source.json'), configs = file.object, data, name = Params.name;\n\n  /**\n   * Sets the missing Boolean members in the data object. @priv\n   * @param {String} name The data source name.\n   * @param {Object} data Data object.\n   */\n  function setBoolMembers(name, data) {\n    const members = {\n      cacheState: true, autoCommitOnReturn: true, rollbackOnReturn: true, testOnBorrow: true,\n      logExpiredConnections: true, lifo: true\n    };\n    Wb.each(members, (k, v) => {\n      data[k] ??= v;\n    });\n  }\n  /**\n   * Check if the name is valid.\n   */\n  function checkName(name) {\n    if (configs[name])\n      Wb.raise(Str.alreadyExists.format(name));\n  }\n\n  /**\n   * Service handler.\n   */\n  function service() {\n    switch (Params.type) {\n      case 'list':\n        //Get all database list\n        Wb.send(Wb.load('wb/ss/dbe.mjs').getDbNames(true));\n        break;\n      case 'get':\n        //Get details\n        data = configs[name];\n        data.text = name;\n        setBoolMembers(name, data);\n        Wb.send(data);\n        break;\n      case 'add':\n        //Add new database\n        checkName(name);\n        data = { driverClassName: 'driverClassName', url: 'url' };\n        configs[name] = data;\n        file.text = Wb.encodePretty(configs);\n        DataSource.setSource(name, Wb.toJava(data));\n        break;\n      case 'delete':\n        if (name == Wb.getConfig('sys.db.defaultSource'))\n          Wb.raise(Str.cannotDelete.format(name));\n        delete configs[name];\n        file.text = Wb.encodePretty(configs);\n        DataSource.removeSource(name);\n        break;\n      case 'copy':\n        //Copy database\n        let newName = Params.newName;\n        checkName(newName);\n        configs[newName] = configs[name];\n        file.text = Wb.encodePretty(configs);\n        DataSource.setSource(newName, Wb.toJava(configs));\n        break;\n      case 'save':\n        //Save configs\n        let oldName = Params.oldName, deferName = oldName != name;\n        if (deferName)\n          checkName(name);\n        data = Wb.decode(Params.data);\n        delete data.text;\n        configs[name] = data;\n        if (deferName)\n          delete configs[oldName];\n        file.text = Wb.encodePretty(configs);\n        DataSource.setSource(name, Wb.toJava(data));\n        if (deferName) {\n          DataSource.removeSource(oldName);\n          if (oldName == Config.defaultSource)\n            Wb.setConfig('sys.db.defaultSource', name);\n        }\n        if (name == Config.defaultSource)\n          DataSource.defaultDataSource = DataSource.getSource(name);\n        break;\n      case 'test':\n        Wb.getConn(name).close();\n        break;\n    }\n  }\n  //Start execute\n  file.lock();\n  try {\n    service();\n  } finally {\n    file.unlock();\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: set-default.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set default database",
    "serverScript": "function main() {\n  /**\n   * Sets the specified database as the default database. @priv\n   */\n  function setDefaultDb() {\n    let defaultSource = Config.defaultSource, destName = Params.name, destSource, script;\n\n    if (defaultSource == Params.name)\n      Wb.raise('The destination is the same as the source database.');\n    script = Wb.Query.removeComments(new Wb.File(true, 'wb/system/db/sqls.sql').text);\n    script = replaceTypes(script, Params.dbType);\n    defaultSource = Wb.getConn(defaultSource);\n    destSource = Wb.getConn(destName);\n    createTables(script, defaultSource, destSource, Wb.getBool('overwrite'));\n    Wb.setConfig('sys.db.defaultSource', destName);\n    //Explicit commit\n    destSource.commit();\n    DataSource.defaultDataSource = DataSource.getSource(destName);\n    SysUtil.reloadSystem(true);\n  }\n\n  /**\n   * Replace the type variable in the script. @priv\n   * @param {String} script The script.\n   * @param {String} dbType The db type.\n   */\n  function replaceTypes(script, dbType) {\n    let types = new Wb.File(true, 'wb/system/db/types.json').object;\n\n    return script.replaceParams(types[dbType]);\n  }\n\n  /**\n   * Check whether the system tables of the specified database are consistent with the script. @priv\n   */\n  function checkTables() {\n    let script, db, tableName, needCreate = false;\n\n    db = Wb.getConn();\n    script = Wb.Query.removeComments(new Wb.File(true, 'wb/system/db/sqls.sql').text);\n    script = replaceTypes(script, Params.dbType);\n    script.split(';').forEach(sql => {\n      sql = sql.trim();\n      if (!sql) return;\n      if (sql.startsWith('create table')) {\n        tableName = sql.substring(sql.indexOf('table') + 5, sql.indexOf('(') - 1).trim();\n        needCreate = !checkConsistent(db, tableName, sql);\n        if (needCreate)\n          Wb.sql(sql, db);\n      } else {\n        if (needCreate)\n          Wb.sql(sql, db);\n      }\n    });\n  }\n\n  /**\n   * Create system tables. @priv\n   * @param {String} script SQL script.\n   * @param {Wb.Connection} source The source connection.\n   * @param {Wb.Connection} dest The dest connection.\n   * @param {Boolean} overwrite Whether to overwrite tables that already exist.\n   */\n  function createTables(script, source, dest, overwrite) {\n    const emptyTables = ['wb_debug_files', 'wb_log'];\n    let tables = script.split(';'), tableName, rs, needCreate = false, len;\n    len = tables.length;\n    tables.forEach((sql, index) => {\n      sql = sql.trim();\n      if (!sql) return;\n      if (!sql.startsWith('create table')) {\n        if (needCreate)\n          Wb.sql(sql, dest);\n        return;\n      }\n      tableName = sql.substring(sql.indexOf('table') + 5, sql.indexOf('(') - 1).trim();\n      Wb.send([index / len * 100, Str.process.format(tableName)], 'sys.dbc');\n      needCreate = false;\n      if (overwrite) {\n        try {\n          Wb.sql('drop table ' + tableName, dest);\n        } catch (e) {\n          //ignore\n        }\n        needCreate = true;\n      } else {\n        needCreate = !checkConsistent(dest, tableName, sql);\n      }\n      if (needCreate) {\n        Wb.sql(sql, dest);\n        if (!emptyTables.includes(tableName)) {\n          try {\n            try {\n              rs = Wb.sqlRS('select * from ' + tableName, source);\n            } catch (ex) {\n              return;\n            }\n            rs.copyTo(tableName, dest);\n            rs.close();\n          } catch (e) {\n            Wb.sql('drop table ' + tableName, dest);\n            Wb.raise(e);\n          }\n        }\n      }\n    });\n  }\n\n  /**\n   * Check whether the target table is consistent with the source table, and an exception is thrown\n   * if it is inconsistent.\n   * @param {Wb.Connection} db Database connection.\n   * @param {String} tableName Table name.\n   * @param {String} sql SQL script.\n   * @return {Boolean} Whether the table exists.\n   */\n  function checkConsistent(db, tableName, sql) {\n    let rs, meta, colCount, oldFields;\n\n    try {\n      rs = Wb.sqlRS('select * from ' + tableName + ' where 1=0', db);\n    } catch (e) {\n      //ignored if the table does not exist\n      return false;\n    }\n    meta = rs.metaData;\n    colCount = meta.getColumnCount();\n    oldFields = sql.substring(sql.indexOf('(') + 1, sql.lastIndexOf(')') - 1).trim().split('\\n');\n    oldFields.forEach((fieldName, i) => {\n      if (i <= colCount) {\n        fieldName = fieldName.trimLeft();\n        fieldName = fieldName.substr(0, fieldName.indexOf(' '));\n        if (!fieldName.equalsIC(meta.getColumnLabel(i + 1)))\n          Wb.raise(Str.tableInconsistent.format(tableName, fieldName));\n      }\n    });\n    if (colCount != oldFields.length)\n      Wb.raise(Str.tableInconsistent.format(tableName, colCount));\n    rs.close();\n    return true;\n  }\n\n  /**\n   * Gets the database type. @priv\n   */\n  function getDbType() {\n    Wb.send(Wb.getConn(Params.name).metaData.getDatabaseProductName());\n  }\n  switch (Params.type) {\n    case 'get-db':\n      getDbType();\n      break;\n    case 'create':\n      checkTables();\n      break;\n    case 'set-default':\n      let lock = new Wb.Lock('dbc.setDefault');\n      try {\n        setDefaultDb();\n      } finally {\n        lock.unlock();\n      }\n      break;\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: dbc.xwl


{
  "title": "@dbc",
  "icon": "data-provider",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Database source configuration"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {String} - Gets the currently selected database name. */\n  get$currentName() {\n    return app.listTree.selection.text;\n  },\n  /**\n   * Fired when the controls value changes.\n   */\n  onChange() {\n    if (!app.stopChange)\n      app.saveBtn.disabled = false;\n  },\n  /**\n   * Open a prompt window for database operations. @priv\n   * @param {String} name Database name, null represents the default db.\n   * @param {String} title The window title.\n   * @param {String} icon The window icon.\n   * @param {Function} callback The callback function of the window.\n   * @param {Boolean} [overwrite] Whether to create an override option checkbox.\n   */\n  promptSetDb(name, title, icon, callback, overwrite) {\n    Wb.ajax({\n      url: xpath + '/set-default&type=get-db',\n      params: { name },\n      success(resp) {\n        const data = [\n          'db2', 'derby', 'dameng', 'firebird', 'h2', 'hsqldb', 'informix', 'kingbase', 'mariadb', 'mysql',\n          'oracle', 'pointbase', 'postgresql', 'sap', 'sqlite', 'sql server', 'sybase', 'teradata'\n        ];\n        let value, items;\n        resp = resp.toLowerCase();\n        value = data.find(item => resp.includes(item));\n        if (!value) {\n          if (resp.includes('sqlserver'))\n            value = 'sql server';\n          else if (resp.includes('da meng') || resp.includes('dm'))\n            value = 'dameng';\n        }\n        items = [{\n          text: Str.dbType, cname: 'select', cid: 'dbType', required: true, forceSelect: true, data, value\n        }];\n        if (overwrite)\n          items.push({ label: Str.overwriteTables, cname: 'check', cid: 'overwrite', showEmptyLabel: true });\n        Wb.prompt({ title, icon }, items, callback);\n      }\n    });\n  },\n  /**\n   * Save data.\n   * @param {Function} [callback] Fired after success.\n   */\n  save(callback) {\n    if (Wb.verify(app.detailPanel)) {\n      Wb.ajax({\n        url: xpath + '/config&type=save',\n        params: { oldName: app.editNode.text, name: app.text.value, data: Wb.getValue(app.detailPanel, true) },\n        success() {\n          app.saveBtn.disabled = true;\n          app.editNode.text = app.text.value;\n          callback?.call();\n        }\n      });\n    }\n  }\n});",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "tags": "",
  "items": [
    {
      "_icon": "plug",
      "text": "socket1",
      "cls": "Wb.Socket",
      "properties": {
        "cid": "socket1",
        "name": "sys.dbc"
      },
      "events": {
        "message": "if (app.acceptProgress)\n  Wb.progress(Wb.decode(e.data));"
      }
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "addBtn",
                "icon": "add",
                "text": "@Str.add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "Wb.prompt({ title: Str.add, icon: 'add' }, { cid: 'name', text: Str.name, required: true }, (values, win) => {\n  Wb.ajax({\n    url: xpath + '/config&type=add',\n    params: values,\n    success() {\n      let node = app.listTree.addData({ text: values.name, _icon: 'database', _leaf: true });\n      node.select();\n      win.close();\n    }\n  });\n});"
              }
            },
            {
              "_icon": "item",
              "text": "copyBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "copyBtn",
                "icon": "copy",
                "text": "@Str.copy"
              },
              "events": {
                "click": "let node = app.listTree.selection, name = node.text;\nWb.prompt({ title: Str.copy + ' - ' + name, icon: 'copy' },\n  { cid: 'newName', text: Str.name, required: true, value: name + '1', selectOnInit: true },\n  (values, win) => {\n    values.name = name;\n    Wb.ajax({\n      url: xpath + '/config&type=copy',\n      params: values,\n      success() {\n        node = app.listTree.insertDataAfter({ text: values.newName, _icon: 'database', _leaf: true }, node);\n        node.select();\n        win.close();\n      }\n    });\n  });"
              }
            },
            {
              "_icon": "item",
              "text": "deleteBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "deleteBtn",
                "icon": "delete",
                "text": "@Str.del",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "let name = app.currentName;\nWb.confirm(Str.deleteConfirm.format(name), f => {\n  Wb.ajax({\n    url: xpath + '/config&type=delete',\n    params: { name },\n    success() {\n      app.saveBtn.disabled = true;\n      app.listTree.delRecords();\n    }\n  });\n});"
              }
            },
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "saveBtn",
                "icon": "save",
                "text": "@Str.save",
                "keys": "Ctrl+S",
                "disabled": "true"
              },
              "events": {
                "click": "app.save();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "testBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "testBtn",
                "icon": "bolt",
                "text": "@Str.test"
              },
              "events": {
                "click": "let name = app.currentName, testFn = f => Wb.ajax({\n  url: xpath + '/config&type=test',\n  params: { name },\n  success() {\n    Wb.tipDone();\n  }\n});\nif (app.saveBtn.disabled)\n  testFn();\nelse\n  Wb.confirm(Str.modifiedConfirm.format(name), f => {\n    app.save(testFn);\n  });\n"
              }
            },
            {
              "_icon": "item",
              "text": "defaultBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "defaultBtn",
                "icon": "favorite",
                "text": "@Str.default",
                "tip": "@Str.setDefaultDb",
                "disabled": "true"
              },
              "events": {
                "click": "let listTree = app.listTree, node = listTree.selection, name = node.text;\napp.promptSetDb(name, Str.setDefaultDb + ' - ' + name, 'favorite', (values, win) => {\n  let overwrite = values.overwrite, doSetDefault = f => {\n    app.acceptProgress = true;\n    Wb.ajax({\n      url: xpath + '/set-default&type=set-default',\n      params: { overwrite, name, dbType: values.dbType },\n      mask: { width: '50vw', delay: 0 },\n      callback() {\n        Wb.unmask();\n      },\n      success() {\n        listTree.cascade(node => {\n          node.icon = node.text == name ? 'favorite' : 'database';\n        });\n        app.defaultBtn.disabled = true;\n        app.acceptProgress = false;\n        win.close();\n      }\n    });\n  };\n  if (overwrite)\n    Wb.confirm(Str.doConfirm.format(Str.overwriteTables), doSetDefault);\n  else\n    doSetDefault();\n}, true);"
              }
            },
            {
              "_icon": "item",
              "text": "createTablesBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "createTablesBtn",
                "icon": "table",
                "text": "@Str.createTables",
                "tip": "@Str.createSysTables"
              },
              "events": {
                "click": "app.promptSetDb(null, Str.createSysTables, 'table', (params, win) => {\n  Wb.ajax({\n    url: xpath + '/set-default&type=create',\n    params,\n    success() {\n      win.close();\n    }\n  });\n});"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "item",
              "text": "refreshBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "refreshBtn",
                "icon": "refresh",
                "text": "@Str.refresh"
              },
              "events": {
                "click": "let listTree = app.listTree;\nif (app.saveBtn.disabled)\n  listTree.loadSelect();\nelse\n  Wb.choose(Str.saveChanges.format(Str.dbc), btn => {\n    if (btn == 'yes') {\n      app.save(f => listTree.loadSelect());\n    } else if (btn == 'no') {\n      app.saveBtn.disabled = true;\n      listTree.loadSelect();\n    }\n  });"
              }
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "listTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "listTree",
            "url": "@xpath + '/config&type=list'",
            "width": "15em",
            "autoSelect": "true"
          },
          "events": {
            "beforeselect": "let panel = app.detailPanel;\n\nif (app.saveBtn.disabled) {\n  Wb.ajax({\n    url: xpath + '/config&type=get',\n    params: {\n      name: item.text\n    },\n    comps: panel,\n    json: true,\n    success(data) {\n      app.stopChange = true;\n      app.editNode = item;\n      Wb.setValue(panel, data, true);\n      app.stopChange = false;\n    }\n  });\n} else {\n  Wb.choose(Str.saveChanges.format(Str.dbc), btn => {\n    if (btn == 'yes') {\n      app.save(f => item.select());\n    } else if (btn == 'no') {\n      app.saveBtn.disabled = true;\n      item.select()\n    }\n  });\n  return false;\n}",
            "select": "app.defaultBtn.disabled = item.icon != 'database';"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "panel",
          "text": "detailPanel",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "detailPanel",
            "flex": "1",
            "layout": "grid1",
            "wideLabel": "true",
            "tableStyle": "true",
            "autoGrid": "up"
          },
          "_expanded": true,
          "events": {
            "ready": "Wb.setEvents(this, 'change', app.onChange);"
          },
          "items": [
            {
              "_icon": "title",
              "text": "title1",
              "cls": "Wb.Title",
              "properties": {
                "cid": "title1",
                "title": "@Str.general"
              }
            },
            {
              "_icon": "text",
              "text": "text",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text",
                "text": "@Str.name",
                "required": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "desc",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "desc",
                "text": "@Str.desc"
              }
            },
            {
              "_icon": "combo",
              "text": "driverClassName",
              "cls": "Wb.Select",
              "properties": {
                "cid": "driverClassName",
                "text": "driverClassName",
                "data": "[\n  'com.microsoft.sqlserver.jdbc.SQLServerDriver',\n  'oracle.jdbc.driver.OracleDriver',\n  'com.mysql.cj.jdbc.Driver',\n  'org.postgresql.Driver',\n  'org.apache.derby.jdbc.EmbeddedDriver',\n  'com.ibm.db2.jdbc.app.DB2Driver',\n  'com.ibm.db2.jdbc.net.DB2Driver',\n  'com.sybase.jdbc.SybDriver',\n  'com.teradata.jdbc.TeraDriver',\n  'org.h2.Driver',\n  'org.sqlite.JDBC',\n  'dm.jdbc.driver.DmDriver',\n  'com.kingbase.Driver',\n  'org.mariadb.jdbc.Driver',\n  'org.apache.hive.jdbc.HiveDriver',\n  'com.sap.db.jdbc.Driver'\n]",
                "required": "true"
              },
              "events": {
                "select": "let urlText = app.url, urlValue = urlText.value;\nif (!urlValue || urlValue == 'url')\n  urlText.valueIndex = item.itemIndex;"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "url",
              "cls": "Wb.Select",
              "_expanded": true,
              "properties": {
                "cid": "url",
                "text": "url",
                "data": "[\n  'jdbc:sqlserver://127.0.0.1:1433;DatabaseName=mydb;encrypt=false;',\n  'jdbc:oracle:thin:@127.0.0.1:1521:mydb',\n  'jdbc:mysql://127.0.0.1:3306/mydb?autoReconnect=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&mysqlEncoding=utf8&nullCatalogMeansCurrent=true',\n  'jdbc:postgresql://127.0.0.1:5432/mydb',\n  'jdbc:derby:$$wb/system/mydb;create=true',\n  'jdbc:db2:mydb',\n  'jdbc:db2://127.0.0.1:6789/mydb',\n  'jdbc:sybase:Tds:127.0.0.1:4100/mydb',\n  'jdbc:teradata://127.0.0.1/database=mydb, tmode=ANSI, charset=UTF8',\n  'jdbc:h2:$$wb/system/mydb',\n  'jdbc:sqlite:$$wb/system/mydb',\n  'jdbc:dm://127.0.0.1:12345/mydb',\n  'jdbc:kingbase://127.0.0.1:54321/mydb',\n  'jdbc:mariadb://127.0.0.1:3306/test',\n  'jdbc:hive2://127.0.0.1:10000/hadoop',\n  'jdbc:sap://127.0.0.1:30015?reconnect=true'\n]",
                "required": "true"
              }
            },
            {
              "_icon": "text",
              "text": "username",
              "cls": "Wb.Text",
              "properties": {
                "cid": "username",
                "text": "username"
              }
            },
            {
              "_icon": "text",
              "text": "password",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "password",
                "text": "password",
                "password": "true",
                "eyeButton": "true"
              }
            },
            {
              "_icon": "text",
              "text": "connectionProperties",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "connectionProperties",
                "text": "connectionProperties"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "disableConnectionPooling",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "disableConnectionPooling",
                "text": "disableConnectionPooling"
              }
            },
            {
              "_icon": "title",
              "text": "title2",
              "cls": "Wb.Title",
              "properties": {
                "cid": "title2",
                "title": "@Str.connection"
              }
            },
            {
              "_icon": "combo",
              "text": "defaultAutoCommit",
              "cls": "Wb.Select",
              "properties": {
                "cid": "defaultAutoCommit",
                "data": "[{ text: 'true', value: true }, { text: 'false', value: false }]",
                "text": "defaultAutoCommit",
                "forceSelect": "true",
                "clearButton": "true"
              }
            },
            {
              "_icon": "combo",
              "text": "defaultReadOnly",
              "cls": "Wb.Select",
              "properties": {
                "cid": "defaultReadOnly",
                "data": "[{ text: 'true', value: true }, { text: 'false', value: false }]",
                "text": "defaultReadOnly",
                "forceSelect": "true",
                "clearButton": "true"
              }
            },
            {
              "_icon": "combo",
              "text": "defaultTransactionIsolation",
              "cls": "Wb.Select",
              "properties": {
                "cid": "defaultTransactionIsolation",
                "data": "[\n  { text: 'none', value: 0 },\n  { text: 'read uncommitted', value: 1 },\n  { text: 'read committed', value: 2 },\n  { text: 'repeatable read', value: 4 },\n  { text: 'serializable', value: 8 }\n]",
                "text": "defaultTransactionIsolation",
                "forceSelect": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "defaultCatalog",
              "cls": "Wb.Text",
              "properties": {
                "cid": "defaultCatalog",
                "text": "defaultCatalog"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "cacheState",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "cacheState",
                "text": "cacheState"
              }
            },
            {
              "_icon": "number-edit",
              "text": "defaultQueryTimeout",
              "cls": "Wb.Number",
              "_expanded": false,
              "properties": {
                "cid": "defaultQueryTimeout",
                "text": "defaultQueryTimeout(s)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "autoCommitOnReturn",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "autoCommitOnReturn",
                "text": "autoCommitOnReturn"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "rollbackOnReturn",
              "cls": "Wb.Toggle",
              "_expanded": false,
              "properties": {
                "cid": "rollbackOnReturn",
                "text": "rollbackOnReturn"
              }
            },
            {
              "_icon": "title",
              "text": "title3",
              "cls": "Wb.Title",
              "_expanded": false,
              "properties": {
                "cid": "title3",
                "title": "@Str.connectionPool"
              }
            },
            {
              "_icon": "number-edit",
              "text": "initialSize",
              "cls": "Wb.Number",
              "properties": {
                "cid": "initialSize",
                "text": "initialSize",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "maxTotal",
              "cls": "Wb.Number",
              "properties": {
                "cid": "maxTotal",
                "text": "maxTotal",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "maxIdle",
              "cls": "Wb.Number",
              "properties": {
                "cid": "maxIdle",
                "text": "maxIdle",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "minIdle",
              "cls": "Wb.Number",
              "properties": {
                "cid": "minIdle",
                "text": "minIdle",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "maxWaitMillis",
              "cls": "Wb.Number",
              "properties": {
                "cid": "maxWaitMillis",
                "text": "maxWaitMillis(ms)",
                "minValue": "-1",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "title",
              "text": "title4",
              "cls": "Wb.Title",
              "properties": {
                "cid": "title4",
                "title": "@Str.validation"
              }
            },
            {
              "_icon": "text",
              "text": "validationQuery",
              "cls": "Wb.Text",
              "properties": {
                "cid": "validationQuery",
                "text": "validationQuery"
              }
            },
            {
              "_icon": "number-edit",
              "text": "validationQueryTimeout",
              "cls": "Wb.Number",
              "properties": {
                "cid": "validationQueryTimeout",
                "text": "validationQueryTimeout(s)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "testOnCreate",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "testOnCreate",
                "text": "testOnCreate"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "testOnBorrow",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "testOnBorrow",
                "text": "testOnBorrow"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "testOnReturn",
              "cls": "Wb.Toggle",
              "_expanded": false,
              "properties": {
                "cid": "testOnReturn",
                "text": "testOnReturn"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "testWhileIdle",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "testWhileIdle",
                "text": "testWhileIdle"
              }
            },
            {
              "_icon": "number-edit",
              "text": "timeBetweenEvictionRunsMillis",
              "cls": "Wb.Number",
              "properties": {
                "cid": "timeBetweenEvictionRunsMillis",
                "text": "timeBetweenEvictionRunsMillis(ms)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "numTestsPerEvictionRun",
              "cls": "Wb.Number",
              "properties": {
                "cid": "numTestsPerEvictionRun",
                "text": "numTestsPerEvictionRun",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "minEvictableIdleTimeMillis",
              "cls": "Wb.Number",
              "properties": {
                "cid": "minEvictableIdleTimeMillis",
                "text": "minEvictableIdleTimeMillis(ms)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "softMinEvictableIdleTimeMillis",
              "cls": "Wb.Number",
              "properties": {
                "cid": "softMinEvictableIdleTimeMillis",
                "text": "softMinEvictableIdleTimeMillis(ms)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "number-edit",
              "text": "maxConnLifetimeMillis",
              "cls": "Wb.Number",
              "properties": {
                "cid": "maxConnLifetimeMillis",
                "text": "maxConnLifetimeMillis(ms)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "logExpiredConnections",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "logExpiredConnections",
                "text": "logExpiredConnections"
              }
            },
            {
              "_icon": "textarea",
              "text": "connectionInitSqls",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "connectionInitSqls",
                "text": "connectionInitSqls",
                "height": "7em"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "lifo",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "lifo",
                "text": "lifo"
              }
            },
            {
              "_icon": "title",
              "text": "title5",
              "cls": "Wb.Title",
              "properties": {
                "cid": "title5",
                "title": "@Str.others"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "poolPreparedStatements",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "poolPreparedStatements",
                "text": "poolPreparedStatements"
              }
            },
            {
              "_icon": "number-edit",
              "text": "maxOpenPreparedStatements",
              "cls": "Wb.Number",
              "properties": {
                "cid": "maxOpenPreparedStatements",
                "text": "maxOpenPreparedStatements",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "accessToUnderlyingConnectionAllowed",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "accessToUnderlyingConnectionAllowed",
                "text": "accessToUnderlyingConnectionAllowed"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "removeAbandonedOnMaintenance",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "removeAbandonedOnMaintenance",
                "text": "removeAbandonedOnMaintenance"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "removeAbandonedOnBorrow",
              "cls": "Wb.Toggle",
              "_expanded": false,
              "properties": {
                "cid": "removeAbandonedOnBorrow",
                "text": "removeAbandonedOnBorrow"
              }
            },
            {
              "_icon": "number-edit",
              "text": "removeAbandonedTimeout",
              "cls": "Wb.Number",
              "properties": {
                "cid": "removeAbandonedTimeout",
                "text": "removeAbandonedTimeout(s)",
                "minValue": "0",
                "decimalCount": "0"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "logAbandoned",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "logAbandoned",
                "text": "logAbandoned"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "abandonedUsageTracking",
              "cls": "Wb.Toggle",
              "_expanded": false,
              "properties": {
                "cid": "abandonedUsageTracking",
                "text": "abandonedUsageTracking"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "fastFailValidation",
              "cls": "Wb.Toggle",
              "_expanded": true,
              "properties": {
                "cid": "fastFailValidation",
                "text": "fastFailValidation"
              }
            },
            {
              "_icon": "textarea",
              "text": "disconnectionSqlCodes",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "disconnectionSqlCodes",
                "text": "disconnectionSqlCodes",
                "height": "7em"
              }
            },
            {
              "_icon": "text",
              "text": "jmxName",
              "cls": "Wb.Text",
              "properties": {
                "cid": "jmxName",
                "text": "jmxName"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: execute-sql.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Execute arbitrary SQL",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let params = { array: true };\n  //If access to blobs is allowed, use the base64 format\n  if (Wb.getBool('blob'))\n    params.blob = 'text';\n  //SQL is encoded using base64, so it needs to be decoded\n  Wb.sendSql(Wb.apply(params, {\n    sql: Wb.decodeBase64(Params.data), db: Params.db, returnObject: true, readonly: true,\n    dateFormat: true, rs: Util.maxRows\n  }));\n}\nmain();"
  },
  "_icon": "module"
}

File name: export.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Export data to the client",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let sql, tableName = Params.tableName,\n    writer = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new Classes.GZIPOutputStream(response.getOutputStream()), 'utf-8'));\n\n  Wb.setContentType(tableName + '.wbt', true);\n  sql = Params.data;\n  if (sql)\n    sql = Wb.decodeBase64(sql);\n  else\n    sql = 'select * from ' + tableName;\n  Wb.sql({\n    sql, db: Params.db, blob: 'text', fn(row) {\n      writer.write(Wb.encode(row));\n      writer.write('\\n');\n    }\n  });\n  writer.close();\n}\nmain();"
  },
  "_icon": "module"
}

File name: import.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Import data to table",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  switch (Wb.getFileExt(Params.file.name)) {\n    case 'wbt':\n      Util.importWbTable();\n      break;\n    case 'xls':\n    case 'xlsx':\n      Util.importExcel();\n      break;\n    case 'txt':\n    case 'csv':\n      Util.importText();\n      break;\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-table.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Table select, save and download",
    "serverScript": "function main() {\n  if (Params.download || Params.save)\n    Wb.sync(Params);\n  else {\n    const Util = Wb.load('./util.mjs');\n    let sql = 'select * from ' + Params.tableName;\n    if (Params.where)\n      sql += ' where ' + Wb.decodeBase64(Params.where);\n    sql += Wb.getOrderSql();\n    Wb.sendRowx({ sql, db: Params.db, dateFormat: true, rs: Util.maxRows });\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-tree.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select database resource list",
    "serverScript": "function main() {\n  const DBE = Wb.load('wb/ss/dbe.mjs');\n  let result, db = Params.db, schem = Params.schem, type = Params.type;\n\n  switch (type) {\n    case 'db':\n      result = DBE.getSchemas(db);\n      break;\n    case 'schem':\n      result = DBE.getCategory(db, schem, Wb.getInt('cateMode'));\n      break;\n    case 'tableCate':\n      result = DBE.getTables(db, schem);\n      break;\n    case 'viewCate':\n      result = DBE.getTables(db, schem, true);\n      break;\n    case 'procCate':\n      result = DBE.getProcs(db, schem);\n      break;\n    case 'proc':\n      result = DBE.getProcColumns(db, schem, Params.procName);\n      break;\n    case 'func':\n      result = DBE.getProcColumns(db, schem, Params.funcName);\n      break;\n    case 'funcCate':\n      result = DBE.getFuncs(db, schem);\n      break;\n    case 'table':\n    case 'view':\n      result = DBE.getColumns(db, schem, Params.tableName, type == 'table');\n      break;\n    default:\n      result = DBE.getDbNames();\n  }\n  Wb.send(result);\n}\nmain();"
  },
  "_icon": "module"
}

File name: dbe.xwl


{
  "title": "@dbe",
  "icon": "database",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Database management tool"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {Number} - The current window number. */\n  index: 1,\n  /** @property {Boolean} - Max returned rows. */\n  maxRows: 1000,\n  /**\n   * Get the currently open tab card.\n   * @return {Wb.Card} The active card.\n   */\n  get$activeCard() {\n    return app.tab.activeCard;\n  },\n  /**\n   * Get the currently open grid.\n   * @return {Wb.Grid} The active grid.\n   */\n  get$activeGrid() {\n    let card = app.activeCard, grid = card?.tab1?.activeCard?.firstItem ?? card?.firstItem;\n    return grid?.isGrid ? grid : null;\n  },\n  /**\n   * Get the currently open editable grid.\n   * @return {Wb.Grid} The edited grid.\n   */\n  get$editGrid() {\n    let grid = app.activeCard?.firstItem;\n    return grid instanceof Wb.Grid ? grid : null;\n  },\n  /**\n   * Adds a new SQL tab for the specified data source.\n   * @param {String} [sql] SQL statements. null does not update the SQL statement.\n   * @param {String} [db] The name of the data source. The default is the currently selected data source name.\n   * @param {Boolean} [newWin] If a window with the same data source already exists, a new window opens. The\n   * default is true.\n   */\n  addSql(sql, db, newWin = true) {\n    let card, editor;\n\n    db ||= app.getKeyName('db');\n    if (!newWin) {\n      card = app.tab.findBy(card => card.db == db && card.sqlCard);\n      if (card) {\n        card.show();\n        if (sql != null)\n          card.editor1.value = sql;\n        return;\n      }\n    }\n    card = {\n      title: 'SQL' + (app.index++) + ' - ' + db,\n      db,\n      sqlCard: true,\n      icon: 'file-sql',\n      layout: 'column',\n      events: {\n        destroy() {\n          this.stopShowError = true;\n          this.ajaxObject?.abort();\n        }\n      },\n      items: [{\n        cname: 'codeEditor',\n        wrapBorder: 0,\n        sqlDb: db,\n        height: '9em',\n        cid: 'editor1',\n        language: 'sql',\n        value: sql ?? '',\n        popupMenu: true,\n        editorConfigs: {\n          minimap: { enabled: false }\n        }\n      }, '||', {\n        flex: 1,\n        cid: 'tab1',\n        cname: 'tab',\n        tabMenu: true,\n        defaults: { closable: null },\n        autoScroll: true,\n        events: {\n          cardchange() {\n            app.propBtn.disabled = !app.activeGrid;\n            app.setExportBtn();\n          }\n        }\n      }]\n    };\n    card = app.tab.add(card);\n    editor = card.editor1 = card.down('editor1');\n    card.tab1 = card.down('tab1');\n    editor.editor.addAction({\n      id: 'runSql',\n      keybindings: [\n        monaco.KeyMod.CtrlCmd | (monaco.KeyCode.Enter)\n      ],\n      label: Str.runSql,\n      run() {\n        app.runSql(card);\n      }\n    });\n    card.show();\n    editor.focus();\n  },\n  /**\n   * Fired before the grid loads.\n   */\n  onGridBeforeLoad() {\n    let grid = this, card = grid.parent;\n    if (card.isModified) {\n      Wb.choose(Str.modifiedConfirm.format(card.tableName), btn => {\n        if (btn == 'yes')\n          app.doSave(card, f => grid.reload());\n        else if (btn == 'no') {\n          Wb.unModified(card);\n          app.setSaveBtns();\n          grid.reload();\n        }\n      });\n      return false;\n    }\n  },\n  /**\n   * Fired when grid data is changed. @priv\n   */\n  onChange() {\n    let me = this, activeCard = app.activeCard, card = me.upBy(p => p.gridCard);\n    if (card) {\n      Wb.setModified(card);\n      app.saveAllBtn.disabled = false;\n      if (card == activeCard)\n        app.saveBtn.disabled = false;\n    }\n  },\n  /**\n   * Sets the save button state.\n   */\n  setSaveBtns() {\n    app.saveBtn.disabled = !app.activeCard?.isModified;\n    app.saveAllBtn.disabled = !app.tab.find(card => card.isModified);\n  },\n  /**\n   * Opens the specified db table for editing.\n   * @param {String} [tableName] The table name to open. Default is currently selected table.\n   * @param {String} [schem] Schem name. The default is the currently selected schem.\n   * @param {String} [db] The name of the data source. The default is the currently selected data source name.\n   * @param {Boolean} [isView] Whether is db view.\n   */\n  openTable(tableName, schem, db, isView) {\n    let card, configs, fullTableName;\n\n    tableName ||= app.getKeyName('tableName');\n    db ||= app.getKeyName('db');\n    schem ||= app.getKeyName('schem');\n    card = app.tab.findBy(card => card.tableName == tableName && card.schem == schem && card.db == db);\n    if (card) {\n      card.show();\n      return;\n    }\n    fullTableName = schem ? (schem + '.' + tableName) : tableName;\n    configs = {\n      title: tableName,\n      xIsView: isView,\n      tabTip: fullTableName + ' - ' + db,\n      tableName,\n      fullTableName,\n      schem,\n      db,\n      gridCard: true,\n      icon: isView ? 'viewport' : 'table',\n      layout: 'fit',\n      events: {\n        beforeclose() {\n          return this.confirmClose(app.doSave);\n        }\n      },\n      items: {\n        cname: 'grid',\n        cid: 'grid1',\n        url: xpath + '/select-table',\n        contextMenu: app.contextMenu,\n        columnsSortable: true,\n        textSelectable: isView,\n        multiSelect: true,\n        exportFilename: tableName,\n        params: { db, tableName: fullTableName, maxRows: app.maxRows },\n        editable: !isView,\n        autoLoad: false,\n        enterAsTab: true,\n        arrowDownAppend: true,\n        events: {\n          beforeload: app.onGridBeforeLoad,\n          editing: app.onChange,\n          itemchange: app.onChange\n        }\n      }\n    };\n    card = app.tab.add(configs);\n    card.grid1 = card.down('grid1');\n    card.show();\n    card.grid1.load();\n  },\n  /**\n   * Gets the key properties of the currently selected node.\n   * @return {String} [name] The property name. Not found returns null.\n   */\n  getKeyName(name) {\n    return app.tree.selection?.upBy(node => node.isNode && node.data[name])?.data[name] ?? null;\n  },\n  /**\n   * Gets the category of the current node.\n   * @return {String} Node category. Not found returns null.\n   */\n  get$category() {\n    return app.tree.selection?.upBy(node => node.isNode && node.data.cateNode)?.data.type ?? null;\n  },\n  /**\n   * Fired when the page is ready.\n   */\n  onReady() {\n    app.tab.setNavigate(app.backBtn, app.forwardBtn);\n    app.contextMenu = Wb.createMenu([app.addBtn, app.delBtn], false);\n  },\n  /**\n   * Adds the SQL execution result to the specified container. @priv\n   * @param {Wb.Tab} tab The container to render data.\n   * @param {String} title The title.\n   * @param {Object} result Render data.\n   */\n  addResult(tab, title, result) {\n    let items = result?.items;\n    if (Wb.isArray(items)) {\n      tab.add({\n        icon: 'grid', title, layout: 'fit', items: {\n          cname: 'grid', columns: result.columns, fields: result.fields, localData: items,\n          textSelectable: true, editable: true\n        }\n      });\n    } else {\n      if (Wb.isObject(result) || Wb.isArray(result))\n        result = Wb.encode(result);\n      else\n        result = String(result);\n      tab.add({ icon: 'component', cls: 'w-padding', autoScroll: true, title, text: result });\n    }\n  },\n  /**\n   * Gets the editor that currently using.\n   * @return {Wb.CodeEditor} The active editor.\n   */\n  get$activeEditor() {\n    return app.tab.activeCard?.editor1;\n  },\n  /**\n   * Sets the loading status of the specified tab. @priv\n   * @param {Wb.Card} card The tab card.\n   * @param {Boolean} status True means loading, false means loading complete.\n   */\n  setLoading(card, status) {\n    let isSqlCard = card.editor1;\n\n    card.icon = status ? 'loading' : (isSqlCard ? 'file-sql' : 'table');\n    card.spin = status;\n  },\n  /**\n   * Runs SQL statements in the specified tab card.\n   * @param {Wb.Card} card The tab card for running sql.\n   * @param {Boolean} [blob] Whether to return blobs.\n   */\n  runSql(card, blob) {\n    if (card.isLoading) {\n      Wb.tipWarn(Str.loading);\n      return;\n    }\n    let editor = card.editor1, tab = card.tab1, sql;\n\n    editor.completeEdit();\n    card.isLoading = true;\n    app.setLoading.delay(app, Wb.configs.maskDelay, card, true);\n    tab.destroyAll();\n    sql = editor.selectedText || editor.value;\n    card.lastSql = sql;\n    card.ajaxObject = Wb.ajax({\n      url: xpath + '/execute-sql',\n      params: {\n        blob,\n        maxRows: app.maxRows,\n        db: card.db,\n        //Prevent potential SQL blocking\n        data: Wb.encodeBase64(sql)\n      },\n      mask: false,\n      json: true,\n      showError: false,\n      callback() {\n        let xhr = card.ajaxObject;\n\n        if (!card.stopShowError && !xhr.ok)\n          Wb.Request.processError(xhr);\n        delete card.ajaxObject;\n        card.isLoading = false;\n        app.setLoading.cancelDelay(app);\n        app.setLoading(card, false);\n      },\n      success(resp) {\n        let hasResult;\n        //Get the results of returns\n        resp.$return?.forEach((item, i) => {\n          hasResult = true;\n          app.addResult(tab, Str.itemX.format(i + 1), item);\n        });\n        //Get output parameters and other results\n        Wb.each(resp, (k, v) => {\n          if (k != '$return') {\n            hasResult = true;\n            app.addResult(tab, k, v);\n          }\n        });\n        if (!hasResult)\n          tab.add({ text: Str.done, cls: 'w-padding' });\n        //hides tabBar if only has one item\n        tab.tabBar.visible = tab.items.length > 1;\n        tab.firstItem?.show();\n      }\n    });\n  },\n  /**\n   * Saves the data for the specified tab. @priv\n   * @param {Wb.Card} card Tab card.\n   * @param {Function} [success] Fired after success.\n   */\n  doSave(card, success) {\n    let grid = card.grid1;\n\n    grid.completeEdit();\n    grid.sync({\n      params: { save: 1 },\n      success() {\n        Wb.unModified(card);\n        app.setSaveBtns();\n        success?.();\n      }\n    });\n  },\n  /**\n   * Save data.\n   * @param {Boolean} [isAll] whether to save all.\n   */\n  save(isAll) {\n    if (isAll) {\n      app.tab.each(card => {\n        if (card.isModified)\n          app.doSave(card);\n      });\n    } else {\n      app.doSave(app.activeCard);\n    }\n  },\n  /**\n   * Rename the selected node.\n   * @param {Boolean} [uppercase] Whether to change the value to uppercase.\n   * -true: change to uppercase.\n   * -false: change to lowercase.\n   * -null: not change. Default value.\n   */\n  rename(uppercase) {\n    let tree = app.tree, row = tree.selection, editor;\n\n    if (!row) {\n      Wb.tipSelect();\n      return;\n    }\n    tree.startEdit(row, 'text', f => false);\n    editor = Wb.Editor.activeEditor;\n    if (uppercase != null) {\n      let value = editor.value;\n      editor.value = uppercase ? value.toUpperCase() : value.toLowerCase();\n    }\n    editor.select();\n  },\n  /**\n   * Import file to the specified table. @priv\n   */\n  importTable() {\n    let card = app.activeCard, tableName = card.fullTableName;\n\n\n    Wb.selectFile(file => {\n      Wb.ajax({\n        url: xpath + '/import',\n        params: { db: card.db, tableName, file },\n        uploadProgress: Str.import + ' - ' + tableName + '...',\n        success() {\n          card.grid1.reload();\n        }\n      });\n    }, '.wbt, .xls, .xlsx, .csv, .txt');\n  },\n  /**\n   * Set export button status.\n   */\n  setExportBtn() {\n    let card = app.activeCard;\n    app.exportBtn.disabled = !(app.activeGrid && (!card.sqlCard || card.tab1.items.length == 1));\n  },\n  /**\n   * Export all data to \"wbt\" format file.\n   */\n  exportData() {\n    let card = app.activeCard, params = { db: card.db }, tableName;\n\n    if (card.sqlCard) {\n      const regex = /(?:FROM|JOIN|INTO)\\s+([\\w.]+)/gi;\n      let sql = card.lastSql, match;\n      while ((match = regex.exec(sql))) {\n        tableName = match[1];\n        break;\n      }\n      tableName ||= 'data';\n      params.data = Wb.encodeBase64(sql);\n    } else {\n      tableName = card.fullTableName;\n    }\n    params.tableName = tableName;\n    Wb.download(xpath + '/export', params);\n  }\n});",
    "beforeunload": "if (!app.saveAllBtn.disabled) {\n  let card = app.tab.find(card => card.isModified), btn = card?.tabButton;\n  if (card) {\n    card.show();\n    btn.highlight();\n    return false;\n  }\n}"
  },
  "tags": "",
  "items": [
    {
      "_icon": "window",
      "text": "filterWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "filterWin",
        "width": "40em",
        "height": "15em",
        "icon": "filter",
        "title": "@Str.filter",
        "layout": "fit",
        "dialog": "true"
      },
      "events": {
        "ok": "let where = app.activeCard.whereSql = app.sqlEditor.value;\napp.editGrid.load({ params: { where: Wb.encodeBase64(where) } });\nthis.close();",
        "ready": "let me = this;\nme.buttonsBar.insert(0, [{\n  text: Str.reset, handler() {\n    app.sqlEditor.value = '';\n    me.downWhole('ok').fireEvent('click');\n  }\n}, '->'])"
      },
      "items": [
        {
          "_icon": "edit",
          "text": "sqlEditor",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "sqlEditor",
            "language": "sql",
            "lineNumbers": "false",
            "minimap": "false"
          }
        }
      ]
    },
    {
      "_icon": "window",
      "text": "propWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "propWin",
        "title": "@Str.property",
        "icon": "property",
        "layout": "column",
        "modal": "true",
        "width": "80em",
        "gap": "true",
        "padding": "true"
      },
      "items": [
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "row",
            "gap": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "tableName",
              "cls": "Wb.Text",
              "properties": {
                "cid": "tableName",
                "text": "@Str.table",
                "readonly": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "fieldNamesText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "fieldNamesText",
                "readonly": "true",
                "flex": "1",
                "text": "@Str.fields"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "propGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "propGrid",
            "pagingBar": "false",
            "showIcon": "true",
            "textSelectable": "true",
            "flex": "1"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fieldNameCol",
                    "fieldName": "fieldName",
                    "width": "-1",
                    "text": "@Str.name"
                  },
                  "text": "fieldNameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "typeCol",
                    "fieldName": "typeName",
                    "width": "10em",
                    "text": "@Str.type"
                  },
                  "text": "typeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "precisionCol",
                    "fieldName": "precision",
                    "width": "10em",
                    "text": "@Str.size",
                    "align": "right"
                  },
                  "text": "precisionCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "scaleCol",
                    "fieldName": "scale",
                    "width": "10em",
                    "text": "@Str.decimal",
                    "align": "right"
                  },
                  "text": "scaleCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "_expanded": true,
      "events": {
        "ready": "app.onReady();"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "newSqlBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "newSqlBtn",
                "text": "@Str.newSql",
                "icon": "file-sql",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "app.addSql();"
              }
            },
            {
              "_icon": "split",
              "text": "runSqlBtn",
              "cls": "Wb.SplitButton",
              "properties": {
                "cid": "runSqlBtn",
                "disabled": "true",
                "icon": "play",
                "keysText": "Ctrl+Enter",
                "text": "@Str.runSql"
              },
              "events": {
                "click": "app.runSql(app.activeCard);"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "getBlobBtn",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "getBlobBtn",
                        "text": "@Str.runSqlBlob",
                        "icon": "cube"
                      },
                      "events": {
                        "click": "app.runSql(app.activeCard, true);"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "addBtn",
                "icon": "add",
                "disabled": "true",
                "keys": "Ctrl+E",
                "tip": "@Str.add",
                "menuText": "@Str.add"
              },
              "events": {
                "click": "let grid = app.editGrid;\ngrid.addRecord(null, e.ctrlMeta ? grid.selection : null);"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "icon": "delete",
                "disabled": "true",
                "keys": "Ctrl+D",
                "tip": "@Str.del",
                "menuText": "@Str.del"
              },
              "events": {
                "click": "app.editGrid.delRecords();"
              }
            },
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "saveBtn",
                "icon": "save",
                "disabled": "true",
                "keys": "Ctrl+S",
                "tip": "@Str.save"
              },
              "events": {
                "click": "app.save();"
              }
            },
            {
              "_icon": "item",
              "text": "saveAllBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "saveAllBtn",
                "icon": "save-all",
                "disabled": "true",
                "keys": "Ctrl+Shift+S",
                "tip": "@Str.saveAll"
              },
              "events": {
                "click": "app.save(true);"
              }
            },
            {
              "_icon": "item",
              "text": "filterBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "filterBtn",
                "icon": "filter",
                "disabled": "true",
                "tip": "@Str.filter"
              },
              "events": {
                "click": "app.filterWin.show();\napp.sqlEditor.value = app.activeCard.whereSql ?? '';"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              },
              "_expanded": true
            },
            {
              "_icon": "item",
              "text": "propBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "propBtn",
                "icon": "property",
                "disabled": "true",
                "tip": "@Str.property"
              },
              "events": {
                "click": "let item, data = [], fieldNames = [], tableName;\napp.activeGrid.leafColumns.forEach(col => {\n  if (col.rowNum) return;\n  item = Wb.applyWith({}, col, ['typeName', 'precision', 'scale', 'required']);\n  item.fieldName = col.rawName ?? col.fieldName;\n  fieldNames.push(item.fieldName);\n  item._icon = col.required ? 'ok' : 'item';\n  data.push(item);\n});\napp.propGrid.data = data;\ntableName = app.activeCard.tableName;\napp.tableName.visible = tableName;\napp.tableName.value = tableName;\napp.fieldNamesText.value = fieldNames.join(', ');\napp.propWin.show();\napp.propWin.center();"
              }
            },
            {
              "_icon": "item",
              "text": "maxRowsBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "maxRowsBtn",
                "icon": "record",
                "tip": "@Str.maxRecords+' ('+app.maxRows+')'"
              },
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "slidebar",
                      "text": "slider1",
                      "cls": "Wb.Slider",
                      "properties": {
                        "cid": "slider1",
                        "width": "12em",
                        "step": "100",
                        "maxValue": "100000",
                        "minValue": "100",
                        "value": "@app.maxRows"
                      },
                      "events": {
                        "enddrag": "app.maxRows = value;\napp.maxRowsBtn.tip = Str.maxRecords + ' (' + value + ')';\nthis.parent.hide();"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "split",
              "text": "renameBtn",
              "cls": "Wb.SplitButton",
              "properties": {
                "cid": "renameBtn",
                "icon": "edit",
                "keys": "F2",
                "tip": "@Str.rename",
                "stopEvent": "false"
              },
              "events": {
                "click": "if (document.activeElement.isInput)\n  return;\napp.rename();\ne.stopEvent();"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "lowercaseBtn",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "lowercaseBtn",
                        "text": "@Str.lowercase",
                        "keys": "Ctrl+Q"
                      },
                      "events": {
                        "click": "app.rename(false);"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "uppercaseBtn",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "uppercaseBtn",
                        "text": "@Str.uppercase",
                        "keys": "Ctrl+U"
                      },
                      "events": {
                        "click": "app.rename(true);"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "item",
              "text": "exportBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "exportBtn",
                "icon": "export",
                "disabled": "true",
                "tip": "@Str.export",
                "handler": "app.exportData"
              }
            },
            {
              "_icon": "item",
              "text": "importBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "importBtn",
                "icon": "import",
                "disabled": "true",
                "handler": "app.importTable",
                "tip": "@Str.import"
              }
            },
            {
              "_icon": "divider",
              "text": "divider3",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider3"
              },
              "_expanded": true
            },
            {
              "_icon": "item",
              "text": "backBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "backBtn",
                "icon": "left4",
                "tip": "@Str.back"
              }
            },
            {
              "_icon": "item",
              "text": "forwardBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "forwardBtn",
                "icon": "right4",
                "tip": "@Str.forward"
              }
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "tree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "tree",
            "width": "20em",
            "icon": "database",
            "title": "@Str.dbList",
            "url": "@xpath + '/select-tree'",
            "autoSelect": "true",
            "autoPostParams": "true",
            "keyWalking": "true"
          },
          "events": {
            "itemdblclick": "if (e.ctrlMeta && item.data.type != 'db') {\n  app.activeEditor?.setter('selectedText', item.text);\n}",
            "itemclick": "let data = item.data, type = data.type;\nif (e.ctrlMeta) {\n  app.addSql(null, app.getKeyName('db'), false);\n} else {\n  if (type == 'table')\n    app.openTable();\n  else if (type == 'view')\n    app.openTable(data.viewName, null, null, true);\n}"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "textCol",
                    "expander": "true",
                    "width": "-1",
                    "fieldName": "text",
                    "render": "//use sub-color to render subText\nlet subText = data.subText, index;\n\nif (data.type == 'field')\n  index = data.fieldIndex + '. ';\nelse\n  index = '';\nif (subText) {\n  let span = el.addTag('span');\n  span.textContent = index + value;\n  span = el.addEl('w-sub-color w-margin-l', 'span');\n  span.textContent = subText;\n} else\n  return index + value;"
                  },
                  "text": "textCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "readonly": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "tools"
              },
              "text": "tools",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "Wb.openNormal('m?xwl=admin/dbc');"
                  },
                  "properties": {
                    "cid": "setItem",
                    "icon": "gear",
                    "tip": "@Str.set"
                  },
                  "text": "setItem",
                  "_expanded": true,
                  "_icon": "item"
                },
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "app.tree.loadSelect();"
                  },
                  "properties": {
                    "cid": "refreshItem",
                    "icon": "refresh",
                    "tip": "@Str.refresh"
                  },
                  "text": "refreshItem",
                  "_expanded": true,
                  "_icon": "item"
                }
              ]
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "tab",
          "text": "tab",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "tab",
            "flex": "1",
            "tabMenu": "true"
          },
          "events": {
            "cardchange": "app.runSqlBtn.disabled = !newCard?.sqlCard;\napp.addBtn.disabled = app.delBtn.disabled = app.importBtn.disabled = !newCard?.gridCard || newCard?.xIsView;\napp.filterBtn.disabled = !newCard?.gridCard\napp.propBtn.disabled = !app.activeGrid;\napp.setExportBtn();\napp.setSaveBtns();"
          }
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add department",
    "serverScript": "function main() {\n  let rec = { sid: Wb.getId() };\n\n  Wb.set(rec);\n  Params.parent_id ||= '0';\n  Wb.sync({ tableName: 'wb_dept', insert: Params });\n  rec.dept_id_disp = rec.sid;\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete departments",
    "serverScript": "function main() {\n  let items, del = [];\n\n  items = Wb.Builder.getTreeItems(Wb.getAllRows('select sid,parent_id from wb_dept'), Wb.getObject('del').pluck('$sid'));\n  items.forEach(item => del.push({ $sid: item }));\n  Wb.sync({ tableName: 'wb_dept', del });\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit department",
    "serverScript": "function main() {\n  Wb.sync({ tableName: 'wb_dept', update: Params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: move.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Move departments",
    "serverScript": "function main() {\n  let update = [], parent_id = Params.parentId || '0';\n\n  Wb.getObject('source').forEach(item => {\n    update.push({ $sid: item, parent_id });\n  });\n  Wb.sync({ tableName: 'wb_dept', update });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-manager.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select department manager",
    "serverScript": "function main() {\n  let sql;\n\n  sql = 'select sid as manager_id,user_name as manager_user,display_name as manager_name from wb_user';\n  if (Params.query) {\n    Wb.setLike('query');\n    sql += ' where (user_name like {?query?} or display_name like {?query?})';\n  }\n  sql += ' order by display_name';\n  Wb.sendRows(sql);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select departments",
    "serverScript": "function main() {\n  let sql, rows, firstItem, search = Params.search;\n\n  sql = `\n        select a.dept_name, a.dept_code, a.sid, a.parent_id, b.sid as user_id,\n        b.display_name as manager_name, a.status, 'folder1' as \"_icon\", a.sid as dept_id_disp,\n        case when (select count(*) from wb_dept c where c.parent_id=a.sid)>0 then 1 else 0 end as items\n        from wb_dept a left join wb_user b on a.manager_id=b.sid\n        `;\n  if (search) {\n    Wb.setLike('search');\n    sql += ' where a.dept_name like {?search?} or a.dept_code like {?search?} or a.sid like {?search?}';\n  } else {\n    Params.parent_id = Params.sid || '0';\n    sql += ' where a.parent_id={?parent_id?}';\n  }\n  sql += Wb.getOrderSql({ manager_name: 'b.display_name' });\n  rows = Wb.getDict({ sql, columns: 'tree', rs: search ? 100 : -1 }, 'wb,');\n  if (!search && !Params.sid && (firstItem = rows.items[0]) && rows.items.length == 1)\n    firstItem._expanded = true;\n  Wb.send(rows);\n}\nmain();"
  },
  "_icon": "module"
}

File name: dept.xwl


{
  "title": "@deptMng",
  "icon": "dept",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Department management tool"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Add new department.\n   * @param {Boolean} [isSibling] Whether to add sibling.\n   */\n  addDept(isSibling) {\n    let data, parentId, node, parentNode, win, deptName, tree = app.tree1;\n\n    node = tree.selection;\n    if (isSibling)\n      parentNode = node?.parent ?? tree;\n    else\n      parentNode = node ?? tree;\n    data = node?.data;\n    if (data) {\n      parentId = isSibling ? data.parent_id : data.sid;\n      deptName = data.dept_name;\n    }\n    win = tree.dictAdd({\n      url: xpath + '/add',\n      failure: app.onFailure\n    }, deptName || null, { parent_id: parentId }, (tree, node) => {\n      node.update({ items: [], _icon: 'folder1' });\n    }, null, null, null, parentNode);\n    if (isSibling) {\n      win.icon = 'insert';\n      win.title = Str.addToSibling;\n      win.subTitle = deptName;\n    }\n    win.down('dept_id_disp').hide();\n  },\n  /**\n   * Method to execute when adding or updating data fails.\n   * @param {String} resp Response text.\n   */\n  onFailure(resp) {\n    Wb.checkExists(resp, 'wb_dept_dept_code', app.tree1.dictWin.down('dept_code'));\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.addDept();"
              }
            },
            {
              "_icon": "item",
              "text": "addToSiblingBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addToSiblingBtn",
                "text": "@Str.addToSibling",
                "icon": "insert"
              },
              "events": {
                "click": "app.addDept(true);"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let win = app.tree1.dictEdit({\n  url: xpath + '/edit',\n  failure: app.onFailure\n}, 'code');\nwin.down('dept_id_disp').show();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.tree1.removeRecords(xpath + '/del', 'dept_name');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.tree1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "tree1",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "tree1",
            "url": "@xpath + '/select'",
            "columnsSortable": "true",
            "multiSelect": "true",
            "sorters": "dept_code",
            "stateId": "wb.dept",
            "pagingBar": "true",
            "defaultColumns": "false",
            "draggable": "{ autoDrop: false }",
            "droppable": "wb.dept",
            "serverExport": "true",
            "keyFields": "sid"
          },
          "events": {
            "itemdrop": "let dest = draggable.dest, parentId;\n\nparentId = draggable.mode == 'append' ? dest.data.sid : dest.parent.data.sid;\nWb.ajax({\n  url: xpath + '/move',\n  params: {\n    source: Wb.Tree.getTopNodes(draggable.source).pluck('data').pluck('sid'),\n    parentId\n  },\n  success() {\n    draggable.acceptDrop();\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: file.xwl


{
  "title": "@fileManager",
  "icon": "file-admin",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "File management tool"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "border": "false"
      },
      "items": [
        {
          "_icon": "module",
          "text": "browserXwl",
          "cls": "Wb.Xwl",
          "properties": {
            "cid": "browserXwl",
            "path": "sys/file/browser.xwl"
          }
        }
      ]
    }
  ]
}

File name: flow.xwl


{
  "title": "@flowMng",
  "icon": "flow1",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Workflow management tool",
    "serverScript": "function main() {\n  Params.flowPathLength = new Wb.File(true, 'wb/system/resource/flow').path.length;\n}\nmain();",
    "links": "[\n  \"wb/libs/x6.js\",\n  \"wb/js/flow-designer.js\",\n  \"wb/js/monaco.js\"\n]"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {String} - Workflow files base path. */\n  basePath: '|wb/system/resource/flow',\n  /** @property {String} - The URL used to get the workflow files path. */\n  getFileUrl: 'm?xwl=sys/file/get-resource',\n  /** @property {Object} - Current actived workflow graph. */\n  get$graph() {\n    return app.tab1.activeCard?.graph;\n  },\n  /** @property {Boolean} - Whether current focus in the input. */\n  get$inInput() {\n    return document.activeElement.isInput;\n  },\n  /** @property {Object} - Current actived workflow designer. */\n  get$flowDesigner() {\n    return app.tab1.activeCard?.flowDesigner;\n  },\n  /** @property {Wb.Card} - Current active card. */\n  get$activeCard() {\n    return app.tab1.activeCard;\n  },\n  /** @property {String} - Root path length of the workflow folder. */\n  flowPathLength: _$flowPathLength$_,\n  /**\n   * Create a new flow file.\n   */\n  newFile() {\n    Wb.promptFile((fullname, win) => {\n      if (!fullname.endsWith('.flw'))\n        fullname += '.flw';\n      Wb.ajax({\n        url: 'm?xwl=sys/file/add',\n        params: { fullname },\n        json: true,\n        success(resp) {\n          app.createCard(null, resp.path, resp.lastModified.dateValue.getTime());\n          win.close();\n        }\n      });\n    }, app.basePath, '.flw', Str.addFile, false, app.getFileUrl);\n  },\n  /**\n   * Create a new flow file.\n   */\n  openFile() {\n    Wb.promptFiles((files, win) => {\n      files.forEach(file => app.doOpenFile(file));\n      win.close();\n    }, app.basePath, '.flw', Str.selectFile, app.getFileUrl);\n  },\n  /**\n   * Find the file tab from the file path.\n   * @param {String} path The file path.\n   * @return {Wb.Card} The found card, returns null if not found.\n   */\n  findCard(path) {\n    return this.tab1.findBy(card => card.path == path);\n  },\n  /**\n   * Open the file at the specified path.\n   * @param {String} path Full file path.\n   * @param {Function} [callback] The callback method after successfully opening.\n   * @param {Wb.Card} callback.card The opened card.\n   */\n  doOpenFile(path, callback) {\n    let card = app.findCard(path);\n    if (card) {\n      card.show();\n      callback?.call(app, card);\n    } else {\n      Wb.ajax({\n        url: 'm?xwl=sys/file/open-resource', params: { path, charset: 'UTF-8' }, success(resp) {\n          let divPos, lastModified, content;\n\n          divPos = resp.indexOf('|');\n          lastModified = resp.substr(0, divPos);\n          content = resp.substr(divPos + 1);\n          if (content)\n            content = Wb.decode(content);\n          app.createCard(content, path, lastModified);\n          callback?.call(app, card);\n        }\n      });\n    }\n  },\n  /**\n   * Create a flow card from the flow data.\n   * @param {Object} [data] Flow data.\n   * @param {String} [path] Flow file path.\n   * @param {Number} [lastModified] File last modified date.\n   * @return {Wb.Card} New created card.\n   */\n  createCard(data, path, lastModified) {\n    let card, tabTip, title, me = this;\n\n    if (Wb.isString(lastModified))\n      lastModified = parseInt(lastModified);\n    if (path) {\n      title = Wb.getFilename(path);\n      tabTip = path.substr(app.flowPathLength + 1);\n    } else {\n      app.fileIndex ??= 1;\n      title = Str.new + ' - ' + (app.fileIndex++)\n      tabTip = undefined;\n    }\n    card = app.tab1.add({\n      title, tabTip, path, lastModified, icon: 'flow1', layout: 'fit',\n      events: {\n        beforeclose() {\n          return this.confirmClose(me.saveCard, me);\n        }\n      }, items: {\n        cname: 'flowDesigner', cid: 'designerComp', events: {\n          change() {\n            app.setModified(this.parent);\n          }\n        }\n      }\n    });\n    card.flowDesigner = card.down('designerComp')\n    card.graph = card.flowDesigner.graph;\n    card.flowDesigner.flowData = data ?? {};\n    card.show();\n    return card;\n  },\n  /**\n   * Save workflow files.\n   * @param {Boolean} [isAll] Whether to save all files.\n   * @param {Function} [callback] The callback function after success.\n   * @param {Boolean} [noConfirm] If the file has been modified, whether to prompt to overwrite it.\n   * @param {Wb.Card} [card] The saved card. Default to the current card.\n   */\n  saveFile(isAll, callback, noConfirm, card) {\n    let data = '', cards = [], content, path, designer, activeCard = card ?? app.activeCard;\n\n    app.tab1.each(card => {\n      if (card.isModified && (isAll || card == activeCard)) {\n        designer = card.flowDesigner;\n        designer.completeEdit();\n        content = Wb.encode(designer.flowData);\n        path = card.path;\n        data += path + '|UTF-8|' + card.lastModified + '|' + content.length + '|' + content;\n        cards.push(card);\n      }\n    });\n    if (data) {\n      Wb.ajax({\n        url: 'm?xwl=sys/file/save', data, json: true,\n        params: {\n          confirm: noConfirm ? 0 : 1\n        },\n        success(lastModified) {\n          cards.forEach((card, i) => {\n            card.lastModified = lastModified[i];\n            app.unModified(card);\n          });\n          callback?.call();\n        },\n        failure(resp) {\n          if (resp.errorCode == 'modified')\n            Wb.confirm(Str.modifiedConfirm.format(Wb.decode(resp.errorText).join(', ')),\n              f => app.saveFile(isAll, callback, true));\n        }\n      });\n    } else {\n      callback?.call();\n    }\n  },\n  /**\n   * Save the file where the specified card has been modified.\n   * @param {Wb.Card} [card] The saved card. Default to the current card.\n   * @param {Function} [callback] The callback method after successfully saving.\n   */\n  saveCard(card, callback) {\n    app.saveFile(false, callback, null, card);\n  },\n  /**\n   * Mark the specified file card modified.\n   * @param {Wb.Card} card File card.\n   */\n  setModified(card) {\n    Wb.setModified(card);\n    if (card == app.activeCard)\n      app.saveItem.disabled = false;\n    app.saveAllItem.disabled = false;\n  },\n  /**\n   * Mark the specified file card unmodified.\n   * @param {Wb.Card} card File card.\n   */\n  unModified(card) {\n    Wb.unModified(card);\n    if (card == app.activeCard)\n      app.saveItem.disabled = true;\n    app.saveAllItem.disabled = !app.tab1.some(t => t.isModified);\n  },\n  /**\n   * Copy the specified cells. @priv\n   * @param {Boolean} [cut] Whether perform cut.\n   */\n  copy(cut) {\n    if (app.inInput)\n      return;\n    let designer = app.flowDesigner, cells = designer.graph.getSelectedCells();\n\n    if (cells.length)\n      app.clipboard = designer[cut ? 'cut' : 'copy'](cells);\n  },\n  /**\n   * Paste clone cells to the current graph.\n   */\n  paste() {\n    if (app.inInput)\n      return;\n    let designer = app.flowDesigner, clipboard = app.clipboard;\n\n    if (clipboard) {\n      let cells = designer.paste(clipboard), graph = designer.graph;\n      graph.cleanSelection();\n      graph.select(cells);\n    }\n  }\n});",
    "beforeunload": "if (!app.saveAllItem.disabled) {\n  let card = app.tab1.find(card => card.isModified), btn = card?.tabButton;\n  if (card) {\n    card.show();\n    btn.highlight();\n    return false;\n  }\n}"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "newItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "newItem",
                "text": "@Str.new",
                "icon": "file",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "app.newFile();"
              }
            },
            {
              "_icon": "item",
              "text": "openItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "openItem",
                "text": "@Str.open",
                "icon": "folder-open",
                "keys": "F3"
              },
              "events": {
                "click": "app.openFile();"
              }
            },
            {
              "_icon": "item",
              "text": "saveItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "saveItem",
                "text": "@Str.save",
                "icon": "save",
                "keys": "Ctrl+S",
                "disabled": "true"
              },
              "events": {
                "click": "app.saveFile();"
              }
            },
            {
              "_icon": "item",
              "text": "saveAllItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "saveAllItem",
                "text": "@Str.saveAll",
                "icon": "save-all",
                "keys": "Ctrl+Shift+S",
                "disabled": "true"
              },
              "events": {
                "click": "app.saveFile(true);"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "cutItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "cutItem",
                "text": "@Str.cut",
                "icon": "cut",
                "keys": "Ctrl+X",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "app.copy(true);"
              }
            },
            {
              "_icon": "item",
              "text": "copyItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "copyItem",
                "text": "@Str.copy",
                "icon": "copy",
                "keys": "Ctrl+C",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "app.copy();"
              }
            },
            {
              "_icon": "item",
              "text": "pasteItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "pasteItem",
                "text": "@Str.paste",
                "icon": "paste",
                "keys": "Ctrl+V",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "app.paste();"
              }
            },
            {
              "_icon": "item",
              "text": "delItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delItem",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Delete",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "if (app.inInput)\n  return;\nlet graph = app.graph, cells = app.graph.getSelectedCells();\n\nif (cells.length)\n  graph.removeCells(cells);"
              }
            },
            {
              "_icon": "item",
              "text": "undoItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "undoItem",
                "text": "@Str.undo",
                "icon": "undo",
                "keys": "Ctrl+Z",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "if (app.inInput)\n  return;\nlet graph = app.graph;\n\nif (graph.canUndo())\n  graph.undo();"
              }
            },
            {
              "_icon": "item",
              "text": "redoItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "redoItem",
                "text": "@Str.redo",
                "icon": "redo",
                "keys": "Ctrl+Y",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "if (app.inInput)\n  return;\nlet graph = app.graph;\n\nif (graph.canRedo())\n  graph.redo();"
              }
            },
            {
              "_icon": "item",
              "text": "selectAllItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "selectAllItem",
                "text": "@Str.selectAll",
                "icon": "panel",
                "keys": "Ctrl+A",
                "stopEvent": "false",
                "disabled": "true"
              },
              "events": {
                "click": "if (app.inInput)\n  return;\nlet graph = app.graph;\ngraph.select(graph.getCells());"
              }
            }
          ]
        },
        {
          "_icon": "tab",
          "text": "tab1",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "tab1",
            "tabMenu": "true"
          },
          "events": {
            "cardchange": "const editButtons = ['cutItem', 'copyItem', 'pasteItem', 'delItem', 'undoItem', 'redoItem', 'selectAllItem'];\nlet isModified = newCard?.isModified;\napp.saveItem.disabled = !isModified;\napp.saveAllItem.disabled = !app.tab1.some(t => t.isModified);\neditButtons.forEach(item => app[item].disabled = !newCard);"
          }
        }
      ]
    }
  ]
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select logs",
    "serverScript": "function main() {\n  let sql, where = [];\n\n  sql = `select a.log_date, b.user_name as mix_user_name,b.display_name as mix_user_name_sub, a.ip, a.log_level, a.log_type, a.msg\n      from wb_log a left join wb_user b on a.user_id=b.sid`;\n  if (Params.beginDt)\n    where.push('log_date >= {?timestamp|beginDt?}');\n  if (Params.endDt)\n    where.push('log_date <= {?timestamp|endDt?}');\n  if (Params.username) {\n    Wb.setLike('username');\n    where.push('(b.user_name like {?username?} or b.display_name like {?username?})');\n  }\n  if (Params.msg) {\n    Wb.setLike('msg');\n    where.push('msg like {?msg?}');\n  }\n  if (Params.ip) {\n    Wb.setLike('ip');\n    where.push('ip like {?ip?}');\n  }\n  if (Params.logLevel)\n    where.push('log_level={?int|logLevel?}');\n  if (Params.logType) {\n    Wb.setLike('logType');\n    where.push('log_type like {?logType?}');\n  }\n  if (where.length) {\n    sql += ' where ' + where.join(' and ');\n  }\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: log.xwl


{
  "title": "@systemLog",
  "icon": "log",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "System log"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Pass the search parameters and reload the grid.\n   */\n  reload() {\n    if (app.stopReload)\n      return;\n    app.grid1.delayLoad({ comps: app.viewport1.tbar });\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "flexWrap": "true",
            "gap": ".1em"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "dt-picker",
              "text": "beginDt",
              "cls": "Wb.Datetime",
              "properties": {
                "cid": "beginDt",
                "placeholder": "@Str.startTime",
                "clearButton": "true",
                "width": "15em"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "dt-picker",
              "text": "endDt",
              "cls": "Wb.Datetime",
              "properties": {
                "cid": "endDt",
                "placeholder": "@Str.endTime",
                "clearButton": "true",
                "width": "15em"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "combo",
              "text": "logLevel",
              "cls": "Wb.Select",
              "properties": {
                "cid": "logLevel",
                "keyName": "logLevel",
                "placeholder": "@Str.logLevel",
                "width": "9em",
                "clearButton": "true",
                "editable": "false"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "text",
              "text": "logType",
              "cls": "Wb.Text",
              "properties": {
                "cid": "logType",
                "placeholder": "@Str.logType",
                "width": "9em",
                "clearButton": "true"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "text",
              "text": "username",
              "cls": "Wb.Text",
              "properties": {
                "cid": "username",
                "placeholder": "@Str.username",
                "width": "9em",
                "clearButton": "true"
              },
              "events": {
                "change": "app.reload();"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "msg",
              "cls": "Wb.Text",
              "properties": {
                "cid": "msg",
                "placeholder": "@Str.content",
                "width": "9em",
                "clearButton": "true"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "text",
              "text": "ip",
              "cls": "Wb.Text",
              "properties": {
                "cid": "ip",
                "placeholder": "@Str.ip",
                "width": "11em",
                "clearButton": "true"
              },
              "events": {
                "change": "app.reload();"
              }
            },
            {
              "_icon": "item",
              "text": "resetItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "resetItem",
                "icon": "undo",
                "text": "@Str.reset"
              },
              "events": {
                "click": "app.stopReload = true;\nWb.reset(app.viewport1.tbar);\napp.stopReload = false;\napp.reload();"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "columnsSortable": "true",
            "sorters": "{name: 'log_date', desc: true}",
            "textSelectable": "true",
            "stateId": "wb.log"
          }
        }
      ]
    }
  ]
}

File name: remove-sessions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Remove sessions",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let del = Wb.getObject('del');\n  del.forEach(item => Sessions.invalidate(item.$userid));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-sessions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select online sessions",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let fromIndex = Wb.getInt('_from'), toIndex = Wb.getInt('_to');\n  Wb.send(Wb.UserUtil.getSessions(Params.userid, fromIndex, toIndex));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-users.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select online users",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let fromIndex = Wb.getInt('_from'), toIndex = Wb.getInt('_to');\n  Wb.send(Wb.UserUtil.getOnlineUsers(fromIndex, toIndex));\n}\nmain();"
  },
  "_icon": "module"
}

File name: online-users.xwl


{
  "title": "@onlineUsers",
  "icon": "earth",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "View online users"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "column"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "grid",
          "text": "userGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "userGrid",
            "url": "@xpath + '/select-users'",
            "autoSelect": "true",
            "textSelectable": "true",
            "flex": "1",
            "multiSelect": "true"
          },
          "events": {
            "selectionchange": "let userid = this.selection?.data.userid;\n\nif (userid)\n  app.sessionGrid.load({ params: { userid } });"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "usernameCol",
                    "fieldName": "username",
                    "width": "12em",
                    "text": "@Str.username",
                    "render": "let ct = data.sessions;\nif (ct > 1)\n  return value + ' (' + ct + ')';\nelse\n  return value;"
                  },
                  "text": "usernameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dispnameCol",
                    "fieldName": "dispname",
                    "width": "12em",
                    "text": "@Str.displayName"
                  },
                  "text": "dispnameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ipCol",
                    "fieldName": "ip",
                    "width": "18em",
                    "text": "@Str.ip"
                  },
                  "text": "ipCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "createTimeCol",
                    "fieldName": "createTime",
                    "width": "13em",
                    "text": "@Str.createTime"
                  },
                  "text": "createTimeCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "lastAccessTimeCol",
                    "fieldName": "lastAccessTime",
                    "width": "13em",
                    "text": "@Str.lastAccessTime"
                  },
                  "text": "lastAccessTimeCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                }
              ]
            },
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "delItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delItem",
                    "text": "@Str.logout",
                    "icon": "delete"
                  },
                  "events": {
                    "click": "app.userGrid.removeRecords(xpath + '/remove-sessions', 'username', null, null, null, Str.logout);"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "grid",
          "text": "sessionGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "sessionGrid",
            "url": "@xpath + '/select-sessions'",
            "autoLoad": "false",
            "textSelectable": "true",
            "height": "14em"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "ipCol",
                    "fieldName": "ip",
                    "width": "18em",
                    "text": "@Str.ip"
                  },
                  "text": "ipCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "createTimeCol",
                    "fieldName": "createTime",
                    "width": "13em",
                    "text": "@Str.createTime"
                  },
                  "text": "createTimeCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "lastAccessTimeCol",
                    "fieldName": "lastAccessTime",
                    "width": "13em",
                    "text": "@Str.lastAccessTime"
                  },
                  "text": "lastAccessTimeCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: clean-invalid.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Clean up invalid permissions",
    "serverScript": "function main() {\n  let params = [];\n\n  Wb.getRows('select module_path from wb_perm', row => {\n    if (!new Wb.File(true, 'wb/modules/' + row.module_path + '.xwl').exists)\n      params.push(row);\n  });\n  Wb.sql({ sql: 'delete from wb_perm where module_path={?module_path?}', params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-modules.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select modules",
    "serverScript": "function main() {\n  const fs = Wb.load('wb/modules/sys/file/fs.mjs');\n  let items, rolesMap = {}, roles;\n\n  Wb.getRows('select module_path,role_id from wb_perm', row => {\n    roles = rolesMap[row.module_path] ??= [];\n    roles.push(row.role_id);\n  });\n  items = fs.listPermModules();\n  Wb.cascade(items, item => {\n    if (item._leaf) {\n      item.roles = rolesMap[item.path];\n    }\n  });\n  Wb.send([{ _checked: false, text: Str.all, _expanded: true, items }]);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-roles.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select roles",
    "serverScript": "function main() {\n  Wb.sendRows(`select sid,role_name as text` + (Params.check ? `,0 as \"_checked\"` : '') +\n    `,'true' as \"_leaf\" from wb_role where sid<>'admin' order by role_name`);\n}\nmain();"
  },
  "_icon": "module"
}

File name: set-permissions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set permissions",
    "serverScript": "function main() {\n  let modules = Params.modules, oldModules, file, roleId = Params.roleId, checked = Wb.getBool('checked');\n\n  Wb.startTrans();\n  if (checked) {\n    let insert = [];\n    oldModules = Wb.getAllRecords('select module_path from wb_perm where role_id={?roleId?}').pluck(0);\n    modules = modules.diff(oldModules);\n    modules.forEach(item => {\n      file = new Wb.File(Wb.File.moduleFolder, item + '.xwl');\n      if (!file.exists)\n        throw Str.notExists.format(item);\n      insert.push({ sid: Wb.getId(), module_path: item, role_id: roleId });\n    });\n    Wb.sync({ tableName: 'wb_perm', insert });\n    modules.forEach(item => Perm.put(item, roleId));\n  } else {\n    let del = [];\n    modules.forEach(item => {\n      del.push({ $module_path: item, $role_id: roleId });\n    });\n    Wb.sync({ tableName: 'wb_perm', del, whereFields: 'module_path,role_id', unique: false });\n    modules.forEach(item => Perm.remove(item, roleId));\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: perm.xwl


{
  "title": "@permMng",
  "icon": "lock",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "System permission management tool"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {String} - The currently selected role ID. */\n  get$roleId() {\n    return app.roleTree1.selectionData.sid;\n  },\n  /**\n   * Sets permissions for the specified list of module nodes.\n   * @param {Wb.TreeItem[]/Wb.TreeItem} nodes List of module nodes.\n   * @param {String} roleId The role id.\n   * @param {Boolean} checked True allows, false prohibits.\n   * @param {Function} [success] Fired after success.\n   */\n  setPerm(nodes, roleId, checked, success) {\n    let len;\n\n    nodes = Wb.toArray(nodes);\n    len = nodes.length;\n    if (len > 1 || !nodes[0].leaf) {\n      let word = len > 1 ? Str.nItems.format(len) : nodes[0].text;\n      word = ' \"' + word + '\" ';\n      Wb.confirm(Str.doConfirm.format(checked ? (Str.permit + word) : (Str.forbid + word)),\n        f => app.doSetPerm(nodes, roleId, checked, success));\n    } else\n      app.doSetPerm(nodes, roleId, checked, success);\n  },\n  /**\n   * Executes Set the permissions that specify the list of module nodes. @priv\n   * @param {Array} nodes List of module nodes.\n   * @param {String} roleId The role id.\n   * @param {Boolean} checked True allows, false prohibits.\n   * @param {Function} [success] Fired after success.\n   */\n  doSetPerm(nodes, roleId, checked, success) {\n    let modules = [], leafNodes = [], oldRoles;\n\n    nodes.forEach(node => {\n      if (node.leaf)\n        leafNodes.push(node);\n      else\n        leafNodes.pushAll(node.downAll(sub => sub.leaf));\n    });\n    leafNodes.forEach(node => modules.push(node.data.path));\n    Wb.ajax({\n      url: xpath + '/set-permissions',\n      //Data can upload more data than params\n      data: { modules, roleId, checked },\n      success() {\n        leafNodes.forEach(node => {\n          if (node.leaf) {\n            oldRoles = node.data.roles ??= [];\n            if (checked) {\n              if (!oldRoles.includes(roleId))\n                oldRoles.push(roleId);\n            } else {\n              oldRoles.remove(roleId);\n            }\n          }\n        });\n        app.refreshPerms();\n        app.refreshRoles();\n        success?.();\n      }\n    });\n  },\n  /**\n   * Updates the check box of the module tree.\n   */\n  refreshPerms() {\n    let tree = app.moduleTree;\n\n    tree.suspendEvent();\n    app.scanPerms(app.roleId, tree, true);\n    tree.resumeEvent();\n  },\n  /**\n   * Updates the check boxes for the role tree.\n   */\n  refreshRoles() {\n    let tree = app.roleTree2, moduleNode = app.moduleTree.selection;\n\n    tree.suspendEvent();\n    tree.each(node => {\n      node.checked = app.scanPerms(node.data.sid, moduleNode);\n    });\n    tree.resumeEvent();\n  },\n  /**\n   * Updates the check box of the module tree. @priv\n   * @param {String} roleId The role id.\n   * @param {Wb.TreeView/Wb.TreeItem} baseNode Specifies that the node and all its child nodes are set.\n   * @param {Boolean} [doCheck] Whether to perform check module nodes.\n   * @return {Boolean} Whether the node and all its child nodes are selected. If both checked and unchecked\n   * nodes exist, return \"half\".\n   */\n  scanPerms(roleId, baseNode, doCheck) {\n    let hasPerm, checked, allChecked = true, allUnchecked = true;\n\n    if (baseNode.leaf) {\n      hasPerm = baseNode.data.roles?.includes(roleId);\n      if (doCheck)\n        baseNode.checked = hasPerm;\n      if (hasPerm)\n        allUnchecked = false;\n      else\n        allChecked = false;\n    } else {\n      baseNode.each(node => {\n        checked = app.scanPerms(roleId, node, doCheck);\n        if (checked == 'half') {\n          allChecked = false;\n          allUnchecked = false;\n        } else if (checked)\n          allUnchecked = false;\n        else\n          allChecked = false;\n      });\n    }\n    if (allChecked)\n      checked = true;\n    else if (allUnchecked)\n      checked = false;\n    else\n      checked = 'half';\n    if (doCheck && baseNode.isNode)\n      baseNode.checked = checked;\n    return checked;\n  },\n  /**\n   * Select the first entry after the tree loads.\n   * @param {Wb.Tree} tree The tree.\n   */\n  selectOnLoad(tree) {\n    if (tree.loaded)\n      tree.firstItem.selected = true;\n    else\n      tree.on('success', f =>\n        tree.firstItem.selected = true\n        , true, { once: true });\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "tree-view",
          "text": "roleTree1",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "roleTree1",
            "width": "20vw",
            "title": "@Str.role",
            "url": "@xpath + '/select-roles'",
            "showIcon": "false",
            "leafExpander": "false",
            "icon": "role"
          },
          "events": {
            "selectionchange": "this.subTitle = this.selection.text;\napp.refreshPerms();"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "tree-view",
          "text": "moduleTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "moduleTree",
            "flex": "1",
            "url": "@xpath + '/select-modules'",
            "multiSelect": "true",
            "allowDeselect": "false",
            "minWidth": "10em",
            "itemIcon": "module"
          },
          "events": {
            "selectionchange": "app.roleTree2.subTitle = this.selection.text;\napp.refreshRoles();",
            "beforecheckchange": "app.setPerm(node, app.roleId, checked);\nreturn false;",
            "success": "app.selectOnLoad(app.roleTree1);"
          },
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "permitItem",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "permitItem",
                    "icon": "ok",
                    "text": "@Str.permit",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "app.setPerm(app.moduleTree.topSelections, app.roleId, true);"
                  }
                },
                {
                  "_icon": "item",
                  "text": "forbidItem",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "forbidItem",
                    "icon": "cancel",
                    "text": "@Str.forbid",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "app.setPerm(app.moduleTree.topSelections, app.roleId, false);"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "item",
                  "text": "expandAll",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "expandAll",
                    "text": "@Str.expandAll",
                    "icon": "expand"
                  },
                  "events": {
                    "click": "app.moduleTree.expandAll();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "collapseAll",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "collapseAll",
                    "text": "@Str.collapseAll",
                    "icon": "compress"
                  },
                  "events": {
                    "click": "app.moduleTree.collapseAll();"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider2",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider2"
                  }
                },
                {
                  "_icon": "item",
                  "text": "clearItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "clearItem",
                    "icon": "recycle",
                    "text": "@Str.clear",
                    "tip": "@Str.clearInvalidData"
                  },
                  "events": {
                    "click": "Wb.ajax({\n  url: xpath + '/clean-invalid',\n  success() {\n    Wb.tipDone(Str.clear);\n  }\n});"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "expanderCol",
                    "fieldName": "text",
                    "expander": "true",
                    "render": "let remark = data.remark;\n\nif (remark) {\n  let span = el.addTag('span');\n  span.textContent = value;\n  span = el.addEl('w-sub-color w-margin-l', 'span');\n  span.textContent = Wb.optText(remark);\n} else {\n  return value;\n}"
                  },
                  "text": "expanderCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "splitter2",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter2"
          }
        },
        {
          "_icon": "tree-view",
          "text": "roleTree2",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "roleTree2",
            "width": "20vw",
            "url": "@xpath + '/select-roles&check=1'",
            "showIcon": "false",
            "leafExpander": "false",
            "title": "@Str.set",
            "icon": "role"
          },
          "events": {
            "beforecheckchange": "app.setPerm(app.moduleTree.selection, node.data.sid, checked, f => {\n  this.suspendEvent();\n  node.checked = checked;\n  this.resumeEvent();\n});\nreturn false;",
            "success": "app.selectOnLoad(app.moduleTree);"
          }
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add role",
    "serverScript": "function main() {\n  let sid = Wb.getId();\n\n  Params.sid = sid;\n  Wb.sync({ tableName: 'wb_role', insert: Params });\n  Wb.send({ sid, role_id_disp: sid });\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete roles",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let del = Wb.getObject('del'), roleName, buffer = Perm.buffer, keys = [];\n\n  del.forEach(item => (roleName = item.$role_name) && Util.isReservedRole(roleName) &&\n    Wb.raise(Str.cannotDelete.format(roleName)));\n  Wb.sync({ tableName: 'wb_role', del });\n  Wb.sql({ sql: 'delete from wb_user_role where role_id={?$sid?}', params: del });\n  Wb.sql({ sql: 'delete from wb_perm where role_id={?$sid?}', params: del });\n  buffer.forEach(k => {\n    if (del.some(item => k.endsWith('|' + item.$sid)))\n      keys.push(k);\n  });\n  keys.forEach(k => buffer.remove(k));\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit role",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let oldRoleName = Params.$role_name;\n  if (Params.role_name && Util.isReservedRole(oldRoleName))\n    Wb.raise(Str.cannotModify.format(Params.role_name));\n  Params.sid = undefined;\n  Wb.sync({ tableName: 'wb_role', update: Params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-modules-by-role.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get modules by role",
    "serverScript": "function main() {\n  let permItems, module, path, fullPath, inRole, text, curText, normalName, scan, icon, img, indexFile, total = 0,\n    rows = [], search = Params.search;\n\n  scan = (folder, pathText) => {\n    folder.each(file => {\n      if (file.isModuleFile && !file.isMinFile) {\n        fullPath = file.modulePath;\n        path = fullPath.slice(0, -4);\n        if (inRole && permItems.includes(path) || !inRole && !permItems.includes(path)) {\n          module = Xwl.get(fullPath);\n          if (module.loginRequired) {\n            text = Wb.optText(module.title);\n            normalName = file.normalName;\n            if (search && !text.includes(search) && !normalName.includes(search))\n              return;\n            total++;\n            icon = module.icon;\n            img = module.img;\n            if (img)\n              img = 'wb/images/' + img + '.png';\n            if (!img && !icon)\n              icon = 'module';\n            rows.push({\n              text: pathText + (text || normalName), subtext: module.remark, path, _icon: icon, _img: img,\n            });\n          }\n        }\n      } else if (file.isFolder) {\n        indexFile = new Wb.File(file, 'index.json');\n        curText = (indexFile.exists ? Wb.optText(indexFile.object.title) : null) || file.name;\n        if (pathText)\n          curText = pathText + curText;\n        scan(file, curText + '/');\n      }\n    });\n  }\n  inRole = Wb.getBool('inRole');\n  permItems = Wb.getAllRecords('select module_path from wb_perm where role_id={?role_id?}')?.pluck(0);\n  scan(Wb.File.moduleFolder, '');\n  rows.mixSort('text');\n  Wb.send({ items: rows.slice(Wb.getInt('_from'), Wb.getInt('_to') + 1), total });\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-users-by-role.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get users by role",
    "serverScript": "let sql;\n\nif (Wb.getBool('inRole')) {\n  sql = `select sid,user_name,display_name from wb_user where sid in\n         (select user_id from wb_user_role where role_id={?role_id?})`;\n} else {\n  sql = `select sid,user_name,display_name from wb_user where sid not in\n         (select user_id from wb_user_role where role_id={?role_id?})`;\n}\nif (Params.search) {\n  Wb.setLike('search');\n  sql += ' and (user_name like {?search?} or display_name like {?search?})';\n}\nWb.sendRows(sql);"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select roles",
    "serverScript": "function main() {\n  let sql = 'select t.*,t.sid as role_id_disp from wb_role t';\n  if (Params.search) {\n    Wb.setLike('search');\n    sql += ' where t.role_name like {?search?} or t.sid like {?search?}';\n  }\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: set-module-roles.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set module roles",
    "serverScript": "let insert = [], del = [], user_id, include = Wb.getBool('include'), role_id = Params.role_id;\n\nParams.items.forEach(item => {\n  if (include)\n    insert.push({ sid: Wb.getId(), module_path: item.path, role_id });\n  else\n    del.push({ $module_path: item.path, $role_id: role_id });\n});\nif (include)\n  Wb.sync({ tableName: 'wb_perm', insert });\nelse\n  Wb.sync({ tableName: 'wb_perm', del, whereFields: 'module_path,role_id' });"
  },
  "_icon": "module"
}

File name: set-user-roles.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set user roles",
    "serverScript": "let insert = [], del = [], user_id, include = Wb.getBool('include'), role_id = Params.role_id;\n\nParams.items.forEach(item => {\n  if (include)\n    insert.push({ sid: Wb.getId(), user_id: item.sid, role_id });\n  else\n    del.push({ $user_id: item.sid, $role_id: role_id });\n});\nif (include)\n  Wb.sync({ tableName: 'wb_user_role', insert });\nelse\n  Wb.sync({ tableName: 'wb_user_role', del, whereFields: 'user_id,role_id' });"
  },
  "_icon": "module"
}

File name: role.xwl


{
  "title": "@roleConfig",
  "icon": "role",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add role"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Fired after insert or update fails.\n   */\n  onFailure(resp) {\n    Wb.checkExists(resp, 'wb_role_role_name', app.grid1.dictWin.down('role_name'));\n  },\n  /** @property {Object} - The edit window configs object. */\n  winConfigs: {\n    width: '45em'\n  },\n  /**\n   * Load the specified box data. @priv\n   * @param {Wb.DualBox} box Dual box component.\n   */\n  loadBoxData(box) {\n    if (!box.visible)\n      return;\n    let data = app.selectedData, srcGrid, destGrid, configs;\n\n    box.disabled = !data || box == app.moduleBox && data?.sid == 'admin';\n    srcGrid = box.sourceGrid;\n    destGrid = box.destGrid;\n    srcGrid.subTitle = destGrid.subTitle = data ? data.role_name : Str.notSelected;\n    if (data) {\n      configs = { params: { role_id: data.sid } };\n      srcGrid.load(configs);\n      destGrid.load(configs);\n    } else {\n      srcGrid.destroyAll();\n      destGrid.destroyAll();\n    }\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "column",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "let win = app.grid1.dictAdd({\n  url: xpath + '/add',\n  failure: app.onFailure\n}, Str.role, null, null, app.winConfigs);\nwin.down('role_id_disp').hide();"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let win = app.grid1.dictEdit({\n  url: xpath + '/edit',\n  failure: app.onFailure\n}, 'role_name', null, null, app.winConfigs);\nwin.down('role_id_disp').show();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'role_name');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider"
              }
            },
            {
              "_icon": "item",
              "text": "userBoxBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "userBoxBtn",
                "active": "false",
                "icon": "user2",
                "text": "@Str.user"
              },
              "events": {
                "toggle": "app.userSplitter.visible = app.userBox.visible = active;\napp.loadBoxData(app.userBox);"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "moduleBoxBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "moduleBoxBtn",
                "active": "false",
                "icon": "module",
                "text": "@Str.module"
              },
              "events": {
                "toggle": "app.moduleSplitter.visible = app.moduleBox.visible = active;\napp.loadBoxData(app.moduleBox);"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "role_name",
            "stateId": "wb.role",
            "keyFields": "sid,role_name",
            "flex": "1"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');",
            "selectionchange": "app.selectedData = app.grid1.selectionData;\napp.loadBoxData(app.userBox);\napp.loadBoxData(app.moduleBox);"
          }
        },
        {
          "_icon": "splitter",
          "text": "userSplitter",
          "cls": "Wb.Splitter",
          "_expanded": true,
          "properties": {
            "cid": "userSplitter",
            "visible": "false"
          }
        },
        {
          "_icon": "dual-box",
          "text": "userBox",
          "cls": "Wb.DualBox",
          "properties": {
            "cid": "userBox",
            "sourceUrl": "@xpath + '/get-users-by-role&inRole=0'",
            "destUrl": "@xpath + '/get-users-by-role&inRole=1'",
            "autoLoad": "false",
            "visible": "false",
            "height": "18em",
            "searchBar": "true",
            "textField": "display_name",
            "subtextField": "user_name"
          },
          "events": {
            "beforemove": "Wb.ajax({\n  url: xpath + '/set-user-roles',\n  data: { items, include, role_id: app.selectedData.sid },\n  success: f => {\n    this.acceptMove();\n  }\n});\nreturn false;"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "sourceGridConfigs",
                "isProperty": "true",
                "title": "@Str.availableUsers"
              },
              "text": "sourceGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "destGridConfigs",
                "isProperty": "true",
                "title": "@Str.selectedUsers"
              },
              "text": "destGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columnsx"
              },
              "text": "columnsx",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 0,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "userNameCol",
                    "fieldName": "user_name",
                    "width": "-1"
                  },
                  "text": "userNameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                }
              ]
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "moduleSplitter",
          "cls": "Wb.Splitter",
          "_expanded": true,
          "properties": {
            "cid": "moduleSplitter",
            "visible": "false"
          }
        },
        {
          "_icon": "dual-box",
          "text": "moduleBox",
          "cls": "Wb.DualBox",
          "properties": {
            "cid": "moduleBox",
            "sourceUrl": "@xpath + '/get-modules-by-role&inRole=0'",
            "destUrl": "@xpath + '/get-modules-by-role&inRole=1'",
            "autoLoad": "false",
            "visible": "false",
            "height": "18em",
            "searchBar": "true"
          },
          "events": {
            "beforemove": "Wb.ajax({\n  url: xpath + '/set-module-roles',\n  data: { items, include, role_id: app.selectedData.sid },\n  success: f => {\n    this.acceptMove();\n  }\n});\nreturn false;"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "sourceGridConfigs",
                "isProperty": "true",
                "title": "@Str.availableModules",
                "showIcon": "true"
              },
              "text": "sourceGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "destGridConfigs",
                "isProperty": "true",
                "title": "@Str.selectedModules",
                "showIcon": "true"
              },
              "text": "destGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            }
          ]
        }
      ]
    }
  ]
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select system information",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let items = [], rt = Runtime.getRuntime(), props = System.getProperties(), env = System.getenv(),\n    addr = Classes.InetAddress.getLocalHost(), total = rt.totalMemory(), free = rt.freeMemory();\n\n  items = [{\n    name: 'Current Time',\n    value: new Date().format(Str.dateTimeFormat)\n  }, {\n    name: 'System startup time',\n    value: new Date(Base.startTime.getTime()).format(Str.dateTimeFormat)\n  }, {\n    name: 'Total Memory',\n    value: total.mb\n  }, {\n    name: 'Used Memory',\n    value: (total - free).mb\n  }, {\n    name: 'Free Memory',\n    value: free.mb\n  }, {\n    name: 'Max Memory',\n    value: rt.maxMemory().mb\n  }, {\n    name: 'Available Processors',\n    value: rt.availableProcessors().intText\n  }, {\n    name: 'Username',\n    value: env?.get('USERNAME')\n  }, {\n    name: 'Computer Name',\n    value: env?.get('COMPUTERNAME')\n  }, {\n    name: 'User Domain',\n    value: env?.get('USERDOMAIN')\n  }, {\n    name: 'Host Address',\n    value: addr.getHostAddress()\n  }, {\n    name: 'Host Name',\n    value: addr.getHostName()\n  }, {\n    name: 'OS Name',\n    value: props.getProperty('os.name')\n  }, {\n    name: 'OS Architecture',\n    value: props.getProperty('os.arch')\n  }, {\n    name: 'OS Version',\n    value: props.getProperty('os.version')\n  }, {\n    name: 'Java Version',\n    value: props.getProperty('java.version')\n  }, {\n    name: 'Java Vendor',\n    value: props.getProperty('java.vendor')\n  }, {\n    name: 'Java Vendor URL',\n    value: props.getProperty('java.vendor.url')\n  }, {\n    name: 'Java Home',\n    value: props.getProperty('java.home')\n  }, {\n    name: 'Java VM Specification Version',\n    value: props.getProperty('java.vm.specification.version')\n  }, {\n    name: 'Java VM Specification Vendor',\n    value: props.getProperty('java.vm.specification.vendor')\n  }, {\n    name: 'Java VM Specification Name',\n    value: props.getProperty('java.vm.specification.name')\n  }, {\n    name: 'Java VM Version',\n    value: props.getProperty('java.vm.version')\n  }, {\n    name: 'Java VM Vendor',\n    value: props.getProperty('java.vm.vendor')\n  }, {\n    name: 'Java VM Name',\n    value: props.getProperty('java.vm.name')\n  }, {\n    name: 'Java Specification Version',\n    value: props.getProperty('java.specification.version')\n  }, {\n    name: 'Java Specification Vendor',\n    value: props.getProperty('java.specification.vendor')\n  }, {\n    name: 'Java Specification Name',\n    value: props.getProperty('java.specification.name')\n  }, {\n    name: 'Java Class Version',\n    value: props.getProperty('java.class.version')\n  }, {\n    name: 'Java Class Path',\n    value: props.getProperty('java.class.path')\n  }, {\n    name: 'Java Library Path',\n    value: props.getProperty('java.library.path')\n  }, {\n    name: 'File Separator',\n    value: Wb.encode(props.getProperty('file.separator'))\n  }, {\n    name: 'Path Separator',\n    value: Wb.encode(props.getProperty('path.separator'))\n  }, {\n    name: 'Line Separator',\n    value: Wb.encode(props.getProperty('line.separator'))\n  }, {\n    name: 'User Account Name',\n    value: props.getProperty('user.name')\n  }, {\n    name: 'User Home Directory',\n    value: props.getProperty('user.home')\n  }, {\n    name: 'User Directory',\n    value: props.getProperty('user.dir')\n  }, {\n    name: 'Java Extension Directories',\n    value: props.getProperty('java.ext.dirs')\n  }, {\n    name: 'Java Temp Directory',\n    value: props.getProperty('java.io.tmpdir')\n  }];\n  Wb.send(items);\n}\nmain();"
  },
  "_icon": "module"
}

File name: sysinfo.xwl


{
  "title": "@sysinfo",
  "icon": "system",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "View system information"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "grid",
      "text": "grid1",
      "cls": "Wb.Grid",
      "properties": {
        "cid": "grid1",
        "full": "true",
        "url": "@xpath + '/select'",
        "textSelectable": "true",
        "reserveScrollbar": "true",
        "pagingBar": "false"
      },
      "_expanded": true,
      "events": {
        "destroy": "clearInterval(app.autoRefreshTimer);"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "refreshItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "refreshItem",
                "icon": "refresh",
                "text": "@Str.refresh"
              },
              "events": {
                "click": "app.grid1.reload();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "toggle1",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "toggle1",
                "tip": "@Str.autoRefresh"
              },
              "events": {
                "change": "if (value) {\n  app.autoRefreshTimer = setInterval(f => {\n    let grid = app.grid1;\n\n    if (!grid.loading)\n      grid.reload();\n  }, 2000)\n} else {\n  clearInterval(app.autoRefreshTimer);\n}"
              }
            }
          ]
        },
        {
          "cls": "Wb.Array",
          "properties": {
            "cid": "columns"
          },
          "text": "columns",
          "_expanded": true,
          "_icon": "array",
          "items": [
            {
              "cls": "Wb.Column",
              "properties": {
                "cid": "rowNumCol",
                "rowNum": "true"
              },
              "text": "rowNumCol",
              "_expanded": true,
              "_icon": "column"
            },
            {
              "cls": "Wb.Column",
              "properties": {
                "cid": "nameCol",
                "fieldName": "name",
                "text": "@Str.name",
                "width": "16em"
              },
              "text": "nameCol",
              "_expanded": true,
              "_icon": "column"
            },
            {
              "cls": "Wb.Column",
              "properties": {
                "cid": "valueCol",
                "text": "@Str.value",
                "fieldName": "value",
                "width": "-1",
                "autoWrap": "break-all"
              },
              "text": "valueCol",
              "_expanded": true,
              "_icon": "column"
            }
          ]
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add task",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let sid = Wb.getId(), rec = { sid }, db = Wb.getConn();\n\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_job', insert: Params, db });\n  JobUtil.reloadJob(sid, db.connection);\n  Wb.send(Wb.apply(rec, Util.getFireTime(sid)));\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete tasks",
    "serverScript": "function main() {\n  let del = Wb.getObject('del');\n\n  del.forEach(item => JobUtil.stopJob(item.$sid));\n  Wb.sync({ tableName: 'wb_job', del });\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit task",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let id = Params.$sid, db = Wb.getConn();\n  Wb.sync({ tableName: 'wb_job', update: Params, db });\n  if (Wb.parseBool(Wb.getx('status'))) {\n    JobUtil.reloadJob(id, db.connection);\n  } else {\n    JobUtil.stopJob(id);\n  }\n  Wb.send(Util.getFireTime(id));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select tasks",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let sql, rows, dateType = { type: \"date\" };\n\n  sql = 'select * from wb_job';\n  if (Params.search) {\n    Wb.setLike('search');\n    sql += ' where job_name like {?search?}';\n  }\n  sql += ' order by status desc,' + Wb.getOrderSql(null, null, false);\n  rows = Wb.getRowx(sql);\n  Wb.apply(rows.fields, { trigger_time: dateType, previous_time: dateType, next_time: dateType });\n  rows.items?.forEach(item => {\n    Wb.apply(item, Util.getFireTime(item.sid));\n  });\n  Wb.send(rows);\n}\nmain();"
  },
  "_icon": "module"
}

File name: set-status.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set tasks status",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let update = Wb.getObject('update'), status = Wb.getBool('status') ? 1 : 0, times = [], id, db = Wb.getConn();\n\n  update.forEach(item => item.status = status);\n  Wb.sync({ tableName: 'wb_job', update, db });\n  if (status) {\n    JobUtil.reloadJobs(update.pluck('$sid'), db.connection);\n  }\n  update.forEach(item => {\n    id = item.$sid;\n    if (!status)\n      JobUtil.stopJob(id);\n    times.push(Util.getFireTime(id));\n  });\n  Wb.send(times);\n}\nmain();"
  },
  "_icon": "module"
}

File name: start-stop-task.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Start or stop tasks",
    "serverScript": "function main() {\n  let status = Wb.getBool('status');\n\n  if (status)\n    JobUtil.startEngine();\n  else\n    JobUtil.shutdownEngine();\n  Wb.setConfig(\"sys.task.enabled\", status);\n}\nmain();"
  },
  "_icon": "module"
}

File name: task.xwl


{
  "title": "@taskMng",
  "icon": "task",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.set('isStartup', JobUtil.isStartup());\n}\nmain();",
    "remark": "Scheduled task management tool"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  isStartup: _$isStartup$_,\n  /**\n   * Set status of the specified tasks.\n   * @param {Boolean} status false disabled, true enabled.\n   */\n  setStatus(status) {\n    let grid = app.grid1, selRecs = grid.selections;\n    if (!selRecs.length) {\n      Wb.tipSelect();\n      return;\n    }\n    selRecs = selRecs.filter(rec => rec.data.status != status);\n    if (!selRecs.length)\n      return;\n    Wb.ajax({\n      url: xpath + '/set-status',\n      json: true,\n      params: { update: selRecs.pluck('originData'), status },\n      success(items) {\n        selRecs.forEach((rec, i) => {\n          items[i].status = status;\n          rec.update(items[i]);\n        });\n      }\n    });\n  },\n  /**\n   * Set the startStop btn text and icon.\n   */\n  setStartStopBtn() {\n    let b = app.isStartup, btn = app.startStopBtn;\n    btn.icon = b ? 'stop' : 'play';\n    btn.text = b ? Str.stop : Str.start;\n  }\n});\n"
  },
  "items": [
    {
      "_icon": "window",
      "text": "editWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "editWin",
        "resetDialog": "true",
        "layout": "form1",
        "width": "70em",
        "tableStyle": "true",
        "icon": "task"
      },
      "items": [
        {
          "_icon": "text",
          "text": "job_name",
          "cls": "Wb.Text",
          "properties": {
            "cid": "job_name",
            "text": "@Str.name",
            "required": "true"
          }
        },
        {
          "_icon": "model",
          "text": "intervalCt",
          "cls": "Wb.ControlCt",
          "properties": {
            "cid": "intervalCt",
            "required": "true",
            "text": "@Str.interval",
            "layout": "grid1",
            "gap": ".5em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "radios",
              "text": "interval_type",
              "cls": "Wb.RadioGroup",
              "properties": {
                "cid": "interval_type",
                "layout": "row",
                "gap": "2em"
              },
              "_expanded": true,
              "events": {
                "change": "let value = this.value;\n\napp.interval_value.visible = [0, 1, 2].includes(value);\napp.trigger_time.visible = value > 2 && value < 7;\napp.trigger_weekday.visible = value == 4;\napp.trigger_day.visible = value == 5 || value == 6;\napp.trigger_month.visible = value == 6;\napp.cron_express.visible = value == 7;"
              },
              "items": [
                {
                  "_icon": "check2",
                  "text": "secondRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "secondRadio",
                    "label": "@Str.s"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "minuteRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "minuteRadio",
                    "label": "@Str.m"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "hourRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "hourRadio",
                    "label": "@Str.h"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "dayRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "dayRadio",
                    "label": "@Str.day"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "weekRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "weekRadio",
                    "label": "@Str.week"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "monthRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "monthRadio",
                    "label": "@Str.month"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "yearRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "yearRadio",
                    "label": "@Str.year"
                  }
                },
                {
                  "_icon": "check2",
                  "text": "cronRadio",
                  "cls": "Wb.Radio",
                  "properties": {
                    "cid": "cronRadio",
                    "label": "Cron"
                  }
                }
              ]
            },
            {
              "_icon": "container",
              "text": "valuesCt",
              "cls": "Wb.Container",
              "_expanded": true,
              "properties": {
                "cid": "valuesCt",
                "layout": "row",
                "gap": "true"
              },
              "items": [
                {
                  "_icon": "number-edit",
                  "text": "interval_value",
                  "cls": "Wb.Number",
                  "_expanded": true,
                  "properties": {
                    "cid": "interval_value",
                    "minValue": "1",
                    "decimalCount": "0",
                    "required": "true",
                    "width": "15em",
                    "text": "@Str.every"
                  }
                },
                {
                  "_icon": "combo",
                  "text": "trigger_month",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "trigger_month",
                    "data": "(f => {\n  let date = new Date(2022, 0, 1), i, months = [],\n    formatter = new Intl.DateTimeFormat(Str.lang, { month: 'long' });\n  for (i = 0; i < 12; i++)\n    months.push({ text: formatter.format(date.addMonth(i)), value: i + 1 });\n  return months;\n})()",
                    "required": "true",
                    "text": "@Str.month",
                    "forceSelect": "true"
                  }
                },
                {
                  "_icon": "combo",
                  "text": "trigger_weekday",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "trigger_weekday",
                    "data": "(f => {\n  let date = new Date(2022, 10, 6), i, days = [],\n    formatter = new Intl.DateTimeFormat(Str.lang, { weekday: 'long' });\n  for (i = 0; i < 7; i++)\n    days.push({ text: formatter.format(date.addDay(i)), value: i + 1 });\n  return days;\n})()",
                    "required": "true",
                    "text": "@Str.week",
                    "forceSelect": "true",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "trigger_day",
                  "cls": "Wb.Number",
                  "_expanded": true,
                  "properties": {
                    "cid": "trigger_day",
                    "required": "true",
                    "text": "@Str.day",
                    "minValue": "-31",
                    "maxValue": "31",
                    "width": "10em",
                    "validator": "if (value == 0)\n  return Str.invalidDate.format(value);"
                  }
                },
                {
                  "_icon": "time",
                  "text": "trigger_time",
                  "cls": "Wb.Time",
                  "properties": {
                    "cid": "trigger_time",
                    "text": "@Str.time",
                    "required": "true",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "text",
                  "text": "cron_express",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "cron_express",
                    "text": "@Str.expression",
                    "required": "true",
                    "flex": "1"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "model",
          "text": "durationCt",
          "cls": "Wb.ControlCt",
          "properties": {
            "cid": "durationCt",
            "gap": "true",
            "text": "@Str.duration"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "dt-picker",
              "text": "begin_date",
              "cls": "Wb.Datetime",
              "properties": {
                "cid": "begin_date",
                "text": "@Str.from"
              }
            },
            {
              "_icon": "dt-picker",
              "text": "end_date",
              "cls": "Wb.Datetime",
              "properties": {
                "cid": "end_date",
                "text": "@Str.to"
              }
            }
          ]
        },
        {
          "_icon": "model",
          "text": "optionCt",
          "cls": "Wb.ControlCt",
          "_expanded": true,
          "properties": {
            "cid": "optionCt",
            "gap": "2em",
            "layout": "row",
            "text": "@Str.options"
          },
          "items": [
            {
              "_icon": "switcher-on",
              "text": "status",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "status",
                "text": "@Str.enabled",
                "value": "true"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "concurrent",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "concurrent",
                "text": "@Str.concurrent",
                "value": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "switcher-on",
              "text": "auto_removed",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "auto_removed",
                "text": "@Str.oneTimeTask"
              }
            }
          ]
        },
        {
          "_icon": "text",
          "text": "job_desc",
          "cls": "Wb.Text",
          "properties": {
            "cid": "job_desc",
            "text": "@Str.desc"
          }
        },
        {
          "_icon": "model",
          "text": "scriptCt",
          "cls": "Wb.ControlCt",
          "_expanded": true,
          "properties": {
            "cid": "scriptCt",
            "text": "@Str.script",
            "flex": "1",
            "layout": "fit",
            "minHeight": "15em",
            "padding": "0",
            "required": "true"
          },
          "items": [
            {
              "_icon": "edit",
              "text": "server_script",
              "cls": "Wb.CodeEditor",
              "properties": {
                "cid": "server_script",
                "minimap": "false",
                "required": "true"
              }
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "let type = app.interval_type;\napp.grid1.insertRecord(\n  {\n    url: xpath + '/add',\n    failure(resp) {\n      Wb.checkExists(resp, 'wb_job_job_name', app.job_name);\n    }\n  }, app.editWin);\nif (type.value == -1)\n  type.value = 3;"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "app.grid1.editRecord(\n  {\n    url: xpath + '/edit',\n    failure(resp) {\n      Wb.checkExists(resp, 'wb_job_job_name', app.job_name);\n    }\n  }, app.editWin, 'job_name');"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'job_name');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "pauseBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "pauseBtn",
                "text": "@Str.pause",
                "icon": "pause"
              },
              "events": {
                "click": "app.setStatus(false);"
              }
            },
            {
              "_icon": "item",
              "text": "resumeBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "resumeBtn",
                "text": "@Str.resume",
                "icon": "resume"
              },
              "events": {
                "click": "app.setStatus(true);"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              },
              "_expanded": true
            },
            {
              "_icon": "item",
              "text": "startStopBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "startStopBtn"
              },
              "events": {
                "click": "Wb.confirm(Str.doConfirm.format(app.startStopBtn.text), f => {\n  Wb.ajax({\n    url: xpath + '/start-stop-task',\n    params: { status: !app.isStartup },\n    success() {\n      app.isStartup = !app.isStartup;\n      app.setStartStopBtn();\n      app.grid1.reload();\n    }\n  });\n});",
                "ready": "app.setStartStopBtn();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider3",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider3"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "job_name",
            "stateId": "wb.task",
            "keyFields": "sid,status"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "numCol",
                    "rowNum": "true"
                  },
                  "text": "numCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "fieldName": "job_name",
                    "width": "15em",
                    "text": "@Str.name",
                    "render": "el.insertCellIcon(data.status ? (data.next_time ? 'play' : 'ok') : 'delete', value);"
                  },
                  "text": "nameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "intervalCol",
                    "fieldName": "interval_type",
                    "width": "17em",
                    "text": "@Str.interval",
                    "render": "let intervalValue = data.interval_value, time = ' (' + data.trigger_time?.timeText + ')', weekday, month;\nswitch (value) {\n  case 0:\n    return Str.every + ' ' + intervalValue + ' ' + Str.s;\n  case 1:\n    return Str.every + ' ' + intervalValue + ' ' + Str.m;\n  case 2:\n    return Str.every + ' ' + intervalValue + ' ' + Str.h;\n  case 3:\n    return Str.every + ' \"' + Str.day + '\" ' + time;\n  case 4:\n    weekday = data.trigger_weekday;\n    return Str.every + ' \"' + app.trigger_weekday.data.find(item => item.value == weekday).text + '\"' + time;\n  case 5:\n    return Str.every + ' \"' + Str.month + '\" ' + data.trigger_day + time;\n  case 6:\n    month = data.trigger_month;\n    return Str.every + ' \"' + app.trigger_month.data.find(item => item.value == month).text + '\" ' + data.trigger_day + time;\n  case 7:\n    return data.cron_express;\n}",
                    "align": "left"
                  },
                  "text": "intervalCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "runTimeCol",
                    "fieldName": "previous_date",
                    "width": "17em",
                    "text": "@Str.runTime",
                    "render": "return Str.previous + Str.labelSeparator + (data.previous_time?.dateTimeText || '') + '\\n' +\n  Str.next + Str.labelSeparator + (data.next_time?.dateTimeText || '');",
                    "autoWrap": "pre",
                    "sortable": "false"
                  },
                  "text": "runTimeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "durationCol",
                    "fieldName": "begin_date",
                    "width": "17em",
                    "text": "@Str.duration",
                    "render": "return Str.from + Str.labelSeparator + (value?.dateTimeText || '') + '\\n' +\n  Str.to + Str.labelSeparator + (data.end_date?.dateTimeText || '');",
                    "autoWrap": "pre"
                  },
                  "text": "durationCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "descCol",
                    "fieldName": "job_desc",
                    "width": "-1",
                    "text": "@Str.desc",
                    "sortable": "false"
                  },
                  "text": "descCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add user",
    "serverScript": "function main() {\n  const UserUtil = Wb.UserUtil;\n  const protectedText = UserUtil.protectedText;\n  let sid = Wb.getId(), create_date = new Date(), login_times = 0;\n\n  if (UserUtil.reservedName.includes(Params.user_name))\n    Wb.raise(Str.cannotModify.format(Params.user_name));\n  Wb.set({ sid, create_date, login_times, password: UserUtil.encryptPassword(Params.password) });\n  Wb.sync({ tableName: 'wb_user', insert: Params });\n  addRoles(sid);\n  Wb.send({ sid, user_id_disp: sid, password: protectedText, confirm_password: protectedText, create_date, login_times });\n}\n/**\n * add user roles.\n * @param {String} user_id User id.\n */\nfunction addRoles(user_id) {\n  let roles = Wb.getObject('roleBox'), insert = [];\n\n  roles.forEach(role_id => insert.push({ sid: Wb.getId(), user_id, role_id }));\n  Wb.sync({ tableName: 'wb_user_role', insert });\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete users",
    "serverScript": "function main() {\n  const reservedName = Wb.UserUtil.reservedName;\n  let del = Wb.getObject('del'), username;\n\n  //Reserved accounts cannot be deleted\n  del.forEach(item => (username = item.$user_name) && reservedName.includes(username) &&\n    Wb.raise(Str.cannotDelete.format(username)));\n  Wb.sync({ tableName: 'wb_user', del });\n  Wb.sql({ sql: 'delete from wb_user_role where user_id={?$sid?}', params: del });\n  //logout\n  del.forEach(item => Sessions.invalidate(item.$sid));\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit user",
    "serverScript": "function main() {\n  const Util = Wb.UserUtil;\n  const protectedText = Util.protectedText;\n  let password = Params.password, oldUserName = Params.$user_name, username = Params.user_name;\n\n  if (username && Util.reservedName.includes(oldUserName))\n    Wb.raise(Str.cannotModify.format(oldUserName));\n  if (username && Util.reservedName.includes(username))\n    Wb.raise(Str.cannotModify.format(username));\n  if (password)\n    Wb.set('password', Util.encryptPassword(password));\n  Wb.sync({ tableName: 'wb_user', update: Params });\n  updateRoles(Params.$sid);\n  Wb.send({ password: protectedText, confirm_password: protectedText });\n}\n/**\n * Update user roles.\n * @param {String} user_id User id.\n */\nfunction updateRoles(user_id) {\n  let roles = Wb.getObject('roleBox'), insert = [], oldUserid = Params.$sid;\n\n  Wb.sql({ sql: 'delete from wb_user_role where user_id={?user_id?}', params: { user_id } });\n  roles.forEach(role_id => insert.push({ sid: Wb.getId(), user_id, role_id }));\n  Wb.sync({ tableName: 'wb_user_role', insert });\n  if (Params.user_name || Params.status == '0') {\n    //logout when changed\n    Sessions.invalidate(oldUserid);\n  } else {\n    //Update the session role\n    let sessions = Sessions.getSessions(oldUserid);\n    if (sessions) {\n      if (Config.getBool('sys.session.addDefaultRole'))\n        roles.push('default');\n      roles = roles.stringArray;\n      sessions.forEach(session => session.setAttribute('sys.roles', roles));\n    }\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-roles.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select all roles and the specified user's roles",
    "serverScript": "function main() {\n  let sourceRoles, destRoles;\n\n  if (Params.userId) {\n    sourceRoles = Wb.getAllRows(`select sid,role_name as text,remark as subtext from wb_role where sid<>'default' and status=1 \n      and sid not in (select role_id from wb_user_role where user_id={?userId?}) order by role_name`);\n    destRoles = Wb.getAllRows(`select sid,role_name as text,remark as subtext from wb_role where sid<>'default' and status=1 \n      and sid in (select role_id from wb_user_role where user_id={?userId?}) order by role_name`);\n  } else {\n    sourceRoles = Wb.getAllRows(`select sid,role_name as text,remark as subtext from wb_role where sid<>'default' and status=1\n     order by role_name`);\n  }\n  Wb.send({ sourceRoles, destRoles });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select users",
    "serverScript": "function main() {\n  const protectedText = Wb.UserUtil.protectedText;\n  let sql;\n\n  sql = `select a.sid, a.user_name, a.display_name, '${protectedText}' as password, '${protectedText}' as confirm_password,\n           a.email, a.mobile_phone, a.use_lang, b.sid as dept_id, b.dept_code as dept_code_hide, b.dept_name as dept_name_tree,\n           a.login_times, a.create_date, a.last_login,a.status, a.sid as user_id_disp\n           from wb_user a left join wb_dept b on a.dept_id=b.sid`;\n  if (Params.search) {\n    Wb.setLike('search');\n    sql += ' where a.user_name like {?search?} or a.display_name like {?search?} or a.sid like {?search?}';\n  }\n  sql += Wb.getOrderSql({ dept_name_tree: 'b.dept_name', user_id_disp: 'a.sid', $: 'a' });\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: user.xwl


{
  "title": "@userMng",
  "icon": "user2",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "User account management tool"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Object} - The edit window configs object. */\n  winConfigs: {\n    tagName: 'form',\n    layout: 'form1',\n    height: '45em',\n    width: '75em',\n    autoScroll: true,\n    manualShow: true,\n    events: {\n      ready() {\n        let win = this, ct = new Wb.Container({ layout: 'grid2', padding: false, autoGrid: true });\n\n        win.down('display_name').inputEl.autocomplete = 'off';\n        win.el.autocomplete = 'off';\n        ct.add(win.items.copy());\n        win.add(ct);\n        win.add({\n          cid: 'roleBox', cname: 'dualBox', flex: 1, minHeight: '10em', url: xpath + '/select-roles',\n          getValue() {\n            return this.destData.pluck('sid');\n          },\n          sourceGridConfigs: {\n            title: Str.availableRoles, tools: {\n              icon: 'refresh', tip: Str.refresh, handler() {\n                let roleBox = this.up('roleBox');\n                app.loadRoles(data => {\n                  let excludeItems = roleBox.destGrid.items;\n\n                  roleBox.sourceData = data.sourceRoles.filter(item => !excludeItems.some(t => t.data.sid == item.sid))\n                });\n              }\n            }\n          }, destGridConfigs: { title: Str.selectedRoles }\n        });\n      },\n      beforeok() {\n        let win = this, pwd = win.down('password');\n        //The two passwords are not the same\n        if (pwd.value != win.down('confirm_password').value) {\n          Wb.warn(Str.pwdNotSame, f => pwd.focus(false, true));\n          return false;\n        }\n      }\n    }\n  },\n  /**\n   * Fired after insert or update fails.\n   */\n  onFailure(resp) {\n    Wb.checkExists(resp, 'wb_user_user_name', app.grid1.dictWin.down('user_name'));\n  },\n  /**\n   * Load available roles.\n   * @param {Function} success The callback function after success.\n   * @param {Array} .items Available roles.\n   * @param {String} [userId] User id for getting dest roles.\n   */\n  loadRoles(success, userId) {\n    Wb.ajax({\n      url: xpath + '/select-roles',\n      json: true,\n      params: { userId },\n      success(data) {\n        success?.(data);\n      }\n    });\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.loadRoles(data => {\n  let win, roleBox;\n\n  win = app.grid1.dictAdd({\n    url: xpath + '/add',\n    failure: app.onFailure\n  }, Str.username, null, null, app.winConfigs);\n  roleBox = win.down('roleBox');\n  roleBox.sourceData = data.sourceRoles;\n  roleBox.destData = [];\n  win.show();\n  win.down('user_id_disp').hide();\n});"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let grid = app.grid1, data = grid.selectionData;\nif (!data) {\n  Wb.tipSelect();\n  return;\n}\napp.loadRoles(data => {\n  let win, roleBox;\n  win = grid.dictEdit({\n    url: xpath + '/edit',\n    failure: app.onFailure\n  }, 'user_name', null, null, app.winConfigs);\n  roleBox = win.down('roleBox');\n  roleBox.sourceData = data.sourceRoles;\n  roleBox.destData = data.destRoles;\n  win.show();\n  win.down('user_id_disp').show();\n}, data.sid);"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'user_name');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "user_name",
            "stateId": "wb.user",
            "keyFields": "sid,user_name"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          }
        }
      ]
    }
  ]
}

File name: save.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Save control data",
    "serverScript": "function main() {\n  new Wb.File(true, 'wb/docs/groups.js').text = 'globalThis.WbControlsGroup=' + Wb.payload + ';';\n}\nmain();"
  },
  "_icon": "module"
}

File name: controls.xwl


{
  "title": "@controlsMng",
  "icon": "container",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/docs/data.js\",\n  \"wb/docs/groups.js\"\n]",
    "remark": "Controls management tool"
  },
  "_icon": "module",
  "events": {
    "initialize": "/**\n * Controls manager.\n */\nWb.apply(app, {\n  /**\n   * Add new folder.\n   */\n  addFolder() {\n    let tree = app.controlTree, node = tree.selection ?? tree.lastItem;\n\n    Wb.prompt(Str.addFolder, { text: Str.name, cid: 'name', required: true }, (values, win) => {\n      let text = values.name;\n\n      if (text == 'Ungrouped') {\n        Wb.tipWarn(Str.nameCannotBe.format(text));\n        return;\n      }\n      node.parent.insertDataAfter({ text, items: [] }, node).select();\n      app.modified();\n      win.close();\n    });\n  },\n  /**\n   * Remove the selected nodes.\n   */\n  remove() {\n    app.controlTree.delRecords();\n    app.modified();\n  },\n  /**\n   * Edit the selected node.\n   */\n  edit() {\n    let node = app.controlTree.selection, text = node.text;\n\n    Wb.prompt(Str.edit + ' - ' + text, { text: Str.name, cid: 'name', required: true, value: text },\n      (values, win) => {\n        node.proxy.text = values.name;\n        app.modified();\n        win.close();\n      });\n  },\n  /**\n   * Set modified status. @priv\n   */\n  modified() {\n    app.saveBtn.disabled = false;\n  },\n  /**\n   * Save current data.\n   */\n  save() {\n    let allData = app.controlTree.data;\n    Wb.cascade(allData, data => {\n      data.sort = undefined;\n      if (data.cls) {\n        data.items = undefined;\n        data._leaf = true;\n      }\n    });\n    Wb.ajax({\n      url: xpath + '/save',\n      data: allData,\n      success() {\n        app.saveBtn.disabled = true;\n      }\n    });\n  },\n  /**\n   * Load controls to the tree. @priv\n   */\n  loadControls() {\n    let tree = app.controlTree, grouped = {}, ungrouped = [], data, cls;\n\n    tree.data = WbControlsGroup;\n    tree.cascade(node => {\n      data = node.data;\n      grouped[data.cls] = true;\n    });\n    Wb.each(WbDocsData, (k, v) => {\n      cls = v.cls;\n      if (v.icon && !grouped[cls] && cls != 'Wb.Module') {\n        ungrouped.push({\n          _icon: v.icon, text: v.cname, cls: k, _leaf: true,\n        });\n      }\n    });\n    tree.downBy(node => node.text == 'Ungrouped').addData(ungrouped);\n  }\n});",
    "finalize": "app.loadControls();",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "_expanded": true,
  "tags": "",
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addFolderBtn",
              "cls": "Wb.Item",
              "properties": {
                "cid": "addFolderBtn",
                "text": "@Str.addFolder",
                "icon": "folder1",
                "handler": "app.addFolder",
                "keys": "Ctrl+E"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "properties": {
                "cid": "editBtn",
                "icon": "edit",
                "text": "@Str.edit",
                "handler": "app.edit",
                "disabled": "true",
                "keys": "Ctrl+J"
              },
              "_expanded": true
            },
            {
              "_icon": "item",
              "text": "removeBtn",
              "cls": "Wb.Item",
              "properties": {
                "cid": "removeBtn",
                "icon": "delete",
                "text": "@Str.del",
                "handler": "app.remove",
                "disabled": "true",
                "keys": "Ctrl+D"
              },
              "_expanded": true
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "_leaf": true,
              "items": [],
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "properties": {
                "cid": "saveBtn",
                "text": "@Str.save",
                "icon": "save",
                "handler": "app.save",
                "disabled": "true",
                "keys": "Ctrl+S"
              }
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "controlTree",
          "cls": "Wb.Tree",
          "properties": {
            "cid": "controlTree",
            "draggable": "true",
            "droppable": "wb.controls",
            "multiSelect": "true",
            "keyWalking": "true"
          },
          "_expanded": true,
          "events": {
            "itemdrop": "app.modified();",
            "selectionchange": "let nodes = this.selections, node = nodes[0];\napp.editBtn.disabled = nodes.length != 1 || node.leaf || node.text == 'Ungrouped';\napp.removeBtn.disabled = !nodes.length;"
          }
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add dictionary item",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let rec = { sid: Wb.getId() };\n\n  Util.checkDuplicate(Params.name, Params.group_name);\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_dict', insert: Params });\n  Util.updateBuffer();\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete dictionary items",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let del = Wb.getObject('del'), rows = Wb.getAllRows('select name,alias,group_name from wb_dict where ' +\n    Wb.Query.make('sid', del.pluck('$sid')));\n\n  Wb.sync({ tableName: 'wb_dict', del });\n  Util.clearBuffer(rows);\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let rows = Wb.getAllRows('select name,alias,group_name from wb_dict where sid={?$sid?}');\n\n  Util.checkDuplicate(Params.name ?? Params.oldName, Params.group_name ?? Params.oldGroup, Params.$sid);\n  Wb.sync({ tableName: 'wb_dict', update: Params });\n  Util.clearBuffer(rows);\n  Util.updateBuffer(true);\n}\nmain();",
    "remark": "Edit dictionary item"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select dictionary items",
    "serverScript": "function main() {\n  let sql = 'select * from wb_dict', where = [];\n\n  if (Params.search) {\n    Wb.setLike('search');\n    where.push(`(name like {?search?} or alias like {?search?} or title like {?search?})`);\n  }\n  if (Params.searchGroup) {\n    Wb.setLike('searchGroup');\n    where.push(`group_name like {?searchGroup?}`);\n  }\n  if (Params.searchContent) {\n    Wb.setLike('searchContent');\n    where.push(`(validate_script like {?searchContent?} or edit_tags like {?searchContent?}\n      or render_script like {?searchContent?} or column_tags like {?searchContent?})`);\n  }\n  if (where.length) {\n    sql += ' where ' + where.join(' and ');\n  }\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: dict.xwl


{
  "title": "@dictConfig",
  "icon": "dict",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/js/monaco.js\"\n]",
    "remark": "Dictionary configuration utility"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Load grid with search controls.\n   */\n  load() {\n    app.grid1.delayLoad({ comps: app.viewport1.tbar });\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.grid1.dictAdd(xpath + '/add', Str.dictConfig);"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let data = app.grid1.selection?.data;\napp.grid1.dictEdit(xpath + '/edit', 'name', { oldName: data?.name, oldGroup: data?.group_name });"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'name');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.name",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.load();"
              }
            },
            {
              "_icon": "text",
              "text": "searchGroup",
              "cls": "Wb.Text",
              "properties": {
                "cid": "searchGroup",
                "clearButton": "true",
                "placeholder": "@Str.groupName",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.load();"
              }
            },
            {
              "_icon": "text",
              "text": "searchContent",
              "cls": "Wb.Text",
              "properties": {
                "cid": "searchContent",
                "clearButton": "true",
                "placeholder": "@Str.content",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.load();"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "name",
            "keyWalking": "name",
            "keyFields": "sid"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          }
        }
      ]
    }
  ]
}

File name: docs.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "['wb/docs/wb-docs.css', 'wb/docs/data.js', 'wb/js/monaco.js', 'wb/docs/wb-docs.js']",
    "remark": "Documents reader",
    "loginRequired": "false"
  },
  "events": {
    "initialize": "Wb.docs.App.initDocs(app);"
  },
  "_icon": "module",
  "title": "@docs",
  "icon": "book1",
  "img": "",
  "hideInMenu": "false"
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "IDE server side common actions",
    "serverScript": "Wb.load('./ide-ss.js');\nlet actions = {\n  /**\n   * Create query modules.\n   */\n  createQuery() {\n    let generator = new Wb.ide.Generator(Params);\n\n    generator.buildQuery();\n  },\n  /**\n   * Create crud modules.\n   */\n  createCRUD() {\n    let generator = new Wb.ide.Generator(Params);\n\n    generator.buildCRUD();\n  },\n  /**\n   * Create source data.\n   */\n  createSource() {\n    let generator = new Wb.ide.Generator(Params), modulePath = Params.modulePath, params = Params.params;\n\n    if (modulePath) {\n      params &&= Wb.parse(params);\n      Wb.invoke(new Wb.File(modulePath).modulePath, params);\n    } else {\n      Wb.send(generator.getSourceData());\n    }\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: checkin.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Check in files to the database",
    "serverScript": "function main() {\n  let file, files = Wb.getObject('files');\n\n  Wb.startTrans();\n  files.forEach(name => {\n    file = new Wb.File(name);\n    if (file.isFile) {\n      importFile(file)\n    } else {\n      file.cascade(file => { file.isFile && importFile(file) });\n    }\n  })\n}\n\n/**\n * Import file to the database. @priv\n * @param {Wb.File} file Import file object.\n */\nfunction importFile(file) {\n  let params = {\n    sid: Wb.getId(), sdate: new Date(), file_path: file.relPath, file_version: Params.version,\n    version_remark: Params.remark, modify_date: file.lastModifiedDate, file_content: file.bytes\n  };\n\n  Wb.sql({\n    sql: 'delete from wb_version where file_path={?file_path?} and file_version={?file_version?}',\n    params\n  });\n  Wb.sql({\n    sql:\n      `insert into wb_version values({?sid?}, {?sdate?}, {?file_path?}, {?file_version?}, {?version_remark?},\n         {?modify_date?}, {?file_content?})`,\n    params\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: compress.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "/**\n * Compress files.\n */\nWb.checkDemo();\nconst buildDate = new Date().textValue, cssConfigs = { restructure: false },\n  obfsConfigs = {\n    compact: true,\n    controlFlowFlattening: false,\n    deadCodeInjection: false,\n    debugProtection: false,\n    debugProtectionInterval: 0,\n    disableConsoleOutput: false,\n    identifierNamesGenerator: 'hexadecimal',\n    log: false,\n    numbersToExpressions: false,\n    renameGlobals: false,\n    selfDefending: false,\n    simplify: true,\n    splitStrings: false,\n    stringArray: true,\n    stringArrayCallsTransform: false,\n    stringArrayCallsTransformThreshold: 0.5,\n    stringArrayEncoding: [],\n    stringArrayIndexShift: true,\n    stringArrayRotate: true,\n    stringArrayShuffle: true,\n    stringArrayWrappersCount: 1,\n    stringArrayWrappersChainedCalls: true,\n    stringArrayWrappersParametersMaxCount: 2,\n    stringArrayWrappersType: 'variable',\n    stringArrayThreshold: 0.75,\n    unicodeEscapeSequence: false\n  },\n  jsConfigs = { compress: false, keep_fnames: true, mangle: true, parse: { bare_returns: true } };\nlet forceObfuscate = Wb.getConfig('sys.app.obfuscate'), client = Params.client;\n/**\n * Compress script\n */\nfunction compress() {\n  const copyrightFiles = ['wb', 'wb-client', 'wb-server', 'wb-docs', 'ide'];\n  let filename, script, minFile, isJs, isMjs, isCss, isXwl, path, object, module, text, obfuscate, modulePath,\n    libPath = new Wb.File(true, 'wb/libs').path, doCompress, rebuild = Wb.getBool('rebuild');\n\n  if (forceObfuscate == 'null')\n    forceObfuscate = null;\n  else\n    forceObfuscate = Wb.parseBool(forceObfuscate);\n  doCompress = file => {\n    path = file.path;\n    if (file.isFolder)\n      return;\n    if (path.startsWith(libPath))\n      return;\n    path = path.substr(Base.pathLen);\n    if (path == 'wb/docs/data.js')\n      return;\n    filename = file.name;\n    isJs = filename.endsWith('.js') && !filename.endsWith('.min.js');\n    isMjs = filename.endsWith('.mjs') && !filename.endsWith('.min.mjs');\n    isCss = filename.endsWith('.css') && !filename.endsWith('.min.css');\n    isXwl = filename.endsWith('.xwl') && !filename.endsWith('.min.xwl');\n    if (isJs || isMjs || isCss || isXwl) {\n      if (isJs)\n        filename = filename.slice(0, -3) + '.min.js';\n      else if (isMjs)\n        filename = filename.slice(0, -4) + '.min.mjs';\n      else if (isCss)\n        filename = filename.slice(0, -4) + '.min.css';\n      else\n        filename = filename.slice(0, -4) + '.min.xwl';\n      minFile = new Wb.File(file.parent, filename);\n      if (rebuild || file.lastModified > minFile.lastModified) {\n        notify(Str.compress + ' ' + path + '...');\n        try {\n          if (isJs || isMjs) {\n            text = file.text;\n            text = compressJs(text, text.includes('/* obfuscate */'));\n            if (copyrightFiles.some(name => name + '.min.js' == filename))\n              text = '/*\\n * WebBuilder Javascript Library\\n * Copyright (c) Geejing, ' +\n                'All Rights Reserved.\\n * https://www.geejing.com\\n * Build Date: ' + buildDate + '\\n */\\n' + text;\n            minFile.text = text;\n          } else if (isCss) {\n            if (!globalThis.csso)\n              Wb.load('wb/libs/csso.js');\n            minFile.text = csso.minify(file.text, cssConfigs).css;\n          } else {\n            modulePath = file.modulePath;\n            if (modulePath) {\n              object = file.object;\n              module = Xwl.get(modulePath);\n              obfuscate = object.properties.obfuscate == 'true';\n              script = module.clientScript;\n              object.properties.links = mergeLinks(object.properties.links, module.libList.toString());\n              if (script)\n                object.events = { initialize: compressJs(script, obfuscate) };\n              script = object.properties.serverScript;\n              if (script)\n                object.properties.serverScript = compressJs(script, obfuscate);\n              if (module.hasStateData)\n                object.stateMap = toJSType(module.stateMap);\n              if (module.hasModuleBind)\n                object.moduleMap = toJSType(module.moduleMap);\n              if (module.hasXwl)\n                object.xwlMap = toJSType(module.xwlMap);\n              delete object.items;\n              minFile.object = object;\n            }\n          }\n        } catch (e) {\n          notify(Str.loadFailed.format(path) + '\\n' + e?.toString(), true);\n          Wb.sleep(500);\n        }\n      }\n    }\n  };\n  Wb.File.appFolder.cascadeSelf(doCompress);\n}\n\n/**\n * Convert HashSet, HashMap to it's JS types.\n */\nfunction toJSType(data) {\n  const XwlData = Java.type('com.wb.tool.XwlData');\n  let result = {};\n\n  data.forEach((k, v) => {\n    if (v instanceof XwlData)\n      v = Wb.applyValue({}, v);\n    result[k] = v;\n  });\n  return result;\n}\n/**\n * Merge links @priv\n * @param {Array} links1 links 1\n * @param {Array} links2 links 2\n * @return {Array} The merged links\n */\nfunction mergeLinks(links1, links2) {\n  let links = [], urls = [], url;\n\n  try {\n    links1 = links1 ? Wb.parse(links1) : [];\n  } catch (e) {\n    links1 = [];\n  }\n  try {\n    links2 = links2 ? Wb.parse(links2) : [];\n  } catch (e) {\n    links2 = [];\n  }\n  links1.pushAll(links2);\n  links1.forEach(object => {\n    if (Wb.isString(object))\n      url = object;\n    else\n      url = object.url;\n    if (!urls.includes(url)) {\n      urls.push(url);\n      links.push(object);\n    }\n  });\n  if (links.length > 0)\n    return Wb.encode(links);\n  else\n    return undefined;\n}\n/**\n * Notify progress\n * @param {String} text message\n * @param {Boolean} failed whether to send failed flag\n */\nfunction notify(text, failed) {\n  let data = { client, text, type: 'notifyProgress' };\n\n  if (text !== true)\n    Wb.info(text);\n  if (failed)\n    data.failed = 1;\n  Wb.send(data, 'sys.ide');\n}\n/**\n * Compress script @priv\n * @param {String} script Script content\n * @param {Boolean} [obfuscate] whether to obfuscate\n * @return {String} compressed script\n */\nfunction compressJs(script, obfuscate) {\n  if (forceObfuscate != null)\n    obfuscate = forceObfuscate;\n  if (obfuscate) {\n    if (!globalThis.JavaScriptObfuscator)\n      Wb.load('wb/libs/obfuscator.js');\n    return JavaScriptObfuscator.obfuscate(script, obfsConfigs).getObfuscatedCode();\n  } else {\n    if (!globalThis.Terser)\n      Wb.load('wb/libs/terser.js');\n    return Terser.minify(script, jsConfigs).code;\n  }\n}\ntry {\n  compress();\n} finally {\n  if (!Wb.getBool('finalUnmask'))\n    notify(true);\n}",
    "remark": "Compress and protect source code"
  },
  "_icon": "module"
}

File name: create-docs.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\n/** @property {DocsCreator} creator Docs creator. @priv */\n/** @property {Number} index Manual docs segment index. @priv */\nlet creator, index = 1;\nfunction main() {\n  const DocsCreator = Wb.load('./docs-creator.mjs');\n  creator = new DocsCreator();\n  processFile(new Wb.File(Wb.File.wbFolder, 'js/wb.js'));\n  ['js', 'ss', 'modules', 'docs'].forEach(folderName => {\n    new Wb.File(Wb.File.wbFolder, folderName).cascade(file => {\n      if (file.path.endsWith('/wb/js/wb.js'))\n        return;\n      processFile(file);\n    }, true, false, true);\n  });\n  creator.build();\n}\n/**\n * Create API docs @priv\n * @param {Wb.File} file doc file.\n */\nfunction processFile(file) {\n  let script, clsName, object, name = file.name, relPath, pos;\n  switch (file.extension) {\n    case 'js':\n    case 'mjs':\n      if (name.endsWith('.min.js') || name.endsWith('.min.mjs'))\n        return;\n      script = file.text;\n      if (script.includes('/**')) {\n        relPath = file.relPath;\n        clsName = relPath.replaceAll('/', '.');\n        if (relPath.startsWith('wb/modules'))\n          clsName = 'Modules.' + clsName.substr(11).beforeItem('.');\n        else\n          clsName = clsName.beforeItem('.').capital;\n        script = script.replaceAll('@class $path', '@class ' + clsName);\n        creator.addScript(script, file.relPath);\n      }\n      break;\n    case 'xwl':\n      if (name.endsWith('.min.xwl'))\n        return;\n      clsName = file.relPath.replaceAll('/', '.');\n      //Get xxx of wb.module.xxx.xwl\n      clsName = clsName.substr(11).beforeItem('.');\n      object = file.object;\n      //serverScript\n      script = object.properties.serverScript;\n      if (script?.includes('/**')) {\n        script = script.replaceAll('@class $path', '@class Modules.' + clsName + '#server')\n        creator.addScript(script, file.relPath);\n      }\n      //clientScript\n      script = extractXwlClient(object);\n      if (script) {\n        script = script.replaceAll('@class $path', '@class Modules.' + clsName + '#client')\n        creator.addScript(script, file.relPath);\n      }\n      break;\n    case 'wd':\n      (\"\\n\" + file.text).split('\\n##').forEach((item, i) => {\n        if (i > 0) {\n          pos = item.indexOf('\\n');\n          creator.addDoc({ cls: item.substr(0, pos), index: index++, title: item.substr(pos + 1) });\n        }\n      });\n      break;\n  }\n}\n/**\n * Extract xwl script @priv\n * @param {Object} object Module file object.\n */\nfunction extractXwlClient(object) {\n  let result = object.events?.initialize ?? '';\n  Wb.cascade([object], item => {\n    Wb.each(item.properties, (k, v) => {\n      if ((item != object || k != 'serverScript') && v.includes('/**')) {\n        if (result)\n          result += '\\n';\n        result += v;\n      }\n    });\n    Wb.each(item.events, (k, v) => {\n      if ((item != object || k != 'initialize') && v.includes('/**')) {\n        if (result)\n          result += '\\n';\n        result += v;\n      }\n    });\n  });\n  return result;\n}\nmain();",
    "remark": "Create documents from the files"
  },
  "_icon": "module",
  "title": ""
}

File name: reload-system.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  SysUtil.reloadSystem(true);\n}\nmain();",
    "remark": "Reload all system resources"
  },
  "_icon": "module",
  "title": ""
}

File name: replace.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let result, fileSearcher, FileSearcher = Wb.load('./file-searcher.mjs');\n\n  fileSearcher = new FileSearcher(Wb.getObject('path'), Params.search, Params.fileType,\n    Wb.getObject('options'), null, Params.replaceTo);\n  result = fileSearcher.searchReplace();\n  Wb.send(result);\n}\nmain();",
    "remark": "Replace text in app folder"
  },
  "_icon": "module",
  "title": ""
}

File name: search.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "/**\n * Search text in app folder.\n */\nWb.checkDemo();\nlet actions = {\n  /**\n   * Search text in the files\n   */\n  searchText() {\n    let result, fileSearcher, FileSearcher = Wb.load('./file-searcher.mjs');\n\n    fileSearcher = new FileSearcher(Wb.getObject('path'), Params.search, Params.fileType, Wb.getObject('options'));\n    result = fileSearcher.searchReplace();\n    Wb.send(result);\n  },\n  /**\n   * Search file name\n   */\n  searchFile() {\n    let name = Params.search, result = [];\n\n    name = '*' + name + '*';\n    Wb.File.appFolder.cascade(file => {\n      if (!file.isMinFile && file.match(name)) {\n        result.push({ path: file.path, content: file.name.htmlText, lineNumber: 1, column: 1 });\n        if (result.length > 999)\n          return false;\n      }\n    });\n    Wb.send(result);\n  },\n  /**\n   * Search url shortcut\n   */\n  searchUrl() {\n    let result = [], object = new Wb.File(true, 'wb/system/url.json').object, key = Params.search, value,\n      modulePath = Base.modulePathText;\n\n    if (value = object['/' + key]) {\n      result.push({ path: modulePath + value, content: key.htmlText, lineNumber: 1, column: 1 });\n      delete object['/' + key];\n    }\n    Wb.each(object, (k, v) => {\n      if (k.includes(key))\n        result.push({ path: modulePath + v, content: k.substr(1).htmlText, lineNumber: 1, column: 1 });\n    });\n    Wb.send(result);\n  },\n  /**\n   * Peek file name\n   */\n  peekFile() {\n    let result = [], query = Params.query, module, text, name, title, notMinFile;\n    Wb.File.appFolder.cascade(file => {\n      name = file.name;\n      notMinFile = !file.isMinFile;\n      if (notMinFile && file.isModuleFile) {\n        title = Wb.optText(file.object.title);\n      } else {\n        title = null;\n      }\n      if (notMinFile && (name.includes(query) || title?.includes(query))) {\n        module = file.inModuleFolder;\n        if (module)\n          text = file.modulePath;\n        else\n          text = file.relPath;\n        result.push({ text, module, subtext: title });\n        if (result.length > 999)\n          return false;\n      }\n    });\n    Wb.send(result);\n  },\n  /**\n   * Search none login modules.\n   */\n  noneLoginModule() {\n    let object, result = [];\n    Wb.File.moduleFolder.cascade(file => {\n      if (file.isModuleFile && !file.isMinFile) {\n        try {\n          object = file.object.properties;\n          if (!object)\n            throw object;\n        } catch (e) {\n          Wb.raise('Invalid module \"' + file.modulePath + '\".')\n        }\n        if (!Wb.parseBool(object.loginRequired ?? true)) {\n          result.push({\n            path: file.path, compPath: object.cid, propName: 'loginRequired',\n            propType: 'properties', content: 'loginRequired', lineNumber: 1, column: 1\n          });\n        }\n      }\n    });\n    Wb.send(result);\n  }\n};\nactions[Params.xaction]();",
    "remark": "Search text in app folder"
  },
  "_icon": "module",
  "title": ""
}

File name: select-debug.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  if (!Config.debug)\n    return;\n  let data, userid = Wb.userid, items = [],\n    map = DebugFiles.getDebugFiles(userid);\n\n  if (map) {\n    map.each((k, v) => {\n      data = { file: k, path: v.debugPath };\n      if (v.context)\n        data.status = 2;\n      else\n        data.status = v.status;\n      items.push(data);\n    })\n    items.sort((a, b) => {\n      let x = 9, y = 9;\n\n      if (a.status == 2)\n        x -= 5;\n      if (b.status == 2)\n        y -= 5;\n      if (a.file < b.file)\n        x--;\n      else\n        y--;\n      return x - y;\n    });\n  }\n  Wb.send({ items });\n}\nmain();",
    "remark": "Select debug module list"
  },
  "_icon": "module",
  "title": ""
}

File name: set-debug.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let files = Wb.getObject('files'),\n    type = Params.type, userid = Wb.userid,\n    types = Wb.has('types') ? Wb.getObject('types') : null;\n\n  files.forEach((file, index) => {\n    switch (types ? types[index] : type) {\n      case 'add':\n        DebugFiles.addDebugFile(userid, file);\n        break;\n      case 'remove':\n        DebugFiles.removeDebugFile(userid, file);\n        break;\n      case 'disable':\n        DebugFiles.setDebugStatus(userid, file, 0);\n        break;\n      case 'enable':\n        DebugFiles.setDebugStatus(userid, file, 1);\n        break;\n      case 'terminate':\n        DebugFiles.terminate(userid, file, true);\n        break;\n      case 'resume':\n        DebugFiles.terminate(userid, file, false);\n        break;\n    }\n  });\n}\nmain();",
    "remark": "Set module debug status"
  },
  "_icon": "module",
  "title": ""
}

File name: view-source.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "View file source code",
    "serverScript": "function main() {\n  let items = Params.file.split(':'), path = items[0], line = parseInt(items[1]), code;\n\n  if (path.endsWith('.xwl'))\n    code = Xwl.get(path).getServerScript();\n  else {\n    //module path first\n    let modulePath = 'wb/modules/' + path\n    if ((new File(Base.path, modulePath)).exists())\n      path = modulePath;\n    code = SourceBuffer.get(path).getCharacters();\n  }\n  code = Wb.encode(code);\n  code = code.replaceAll('</script>', '<\\\\/script>');\n  Wb.set({ line, code });\n}\nmain();"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "finalize": "app.codeEditor1.cursor = { lineNumber: _$line$_, column: 1 };"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "edit",
          "text": "codeEditor1",
          "cls": "Wb.CodeEditor",
          "_expanded": true,
          "properties": {
            "cid": "codeEditor1",
            "value": "@_$code$_",
            "wrapBorder": "0",
            "readonly": "true"
          },
          "events": {
            "cursorchange": "app.cursorLabel.text = cursor ? (cursor.lineNumber + ' : ' + cursor.column) : Wb.nbsp;"
          }
        },
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "bbar",
            "isProperty": "true",
            "justify": "end"
          },
          "text": "bbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "label",
              "text": "cursorLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "cursorLabel",
                "text": "1 : 1"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: ide.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('./ide/util.mjs');\n  const idePath = 'wb/modules/dev/ide/';\n  Wb.set('args', Wb.encode({\n    path: Base.pathText,\n    devTools: Wb.getConfig('sys.ss.devTools'),\n    osCharset: Base.osCharset?.toUpperCase(),\n    charset: System.getProperty('file.encoding')?.toUpperCase(),\n    fileCharset: Wb.getConfig('sys.file.charset')?.toUpperCase(),\n    createWbCss: new Wb.File(true, idePath + 'create-wb-css.xwl').exists,\n    createDocs: new Wb.File(true, idePath + 'create-docs.xwl').exists,\n    createRelease: new Wb.File(true, idePath + 'create-release.xwl').exists,\n    compressScript: new Wb.File(true, idePath + 'compress.xwl').exists,\n    releasePath: Wb.getConfig('sys.app.releasePath'),\n    version: Wb.getConfig('sys.app.version'),\n    heartbeat: Wb.getConfig('sys.session.heartbeatInterval'),\n    username: Wb.username,\n    openExample: Params.openExample,\n    openModule: Params.openModule,\n    imgs: Util.getImgs(),\n    icons: Util.getIcons(),\n    ssList: Util.getSsList(),\n    globalsList: Util.getGlobalsList()\n  }));\n}\nmain();",
    "links": "['wb/js/monaco.js', 'wb/docs/data.js', 'wb/docs/groups.js', 'wb/js/ide.js']",
    "remark": "Integrated development environment"
  },
  "events": {
    "initialize": "let obj = new Wb.ide.IDE(parentContainer, _$args$_);\nif (parentContainer)\n  app.app = obj;\nelse\n  globalThis.app = obj;"
  },
  "_icon": "module",
  "title": "@ide",
  "icon": "devtool",
  "img": "",
  "hideInMenu": "false",
  "_expanded": true,
  "tags": "{url: 'ide', newWin: true}"
}

File name: add-type.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add key type",
    "serverScript": "function main() {\n  let rec = { sid: Wb.getId() };\n\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_key_type', insert: Params, send: rec });\n}\nmain();"
  },
  "_icon": "module"
}

File name: del-types.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete key types",
    "serverScript": "function main() {\n  let del = Wb.getObject('del');\n\n  Wb.sync({ tableName: 'wb_key_type', del });\n  Wb.sql({ sql: 'delete from wb_key where rid={?$sid?}', params: del });\n  del.forEach(item => { KVBuffer.buffer.remove(item.$key_name) });\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit-type.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Edit key type",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let oldKeyName = Params.$key_name;\n\n  Wb.sync({ tableName: 'wb_key_type', update: Params });\n  KVBuffer.buffer.remove(oldKeyName);\n  Util.loadKey(Params.key_name || oldKeyName);\n}\nmain();"
  },
  "_icon": "module"
}

File name: save-list.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Save key list",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let sid, rid = Params.keyId, insert = [], rec;\n\n  Params.insert?.forEach(item => {\n    sid = Wb.getId();\n    rec = { sid, rid };\n    Wb.apply(item, rec);\n    insert.push(rec);\n  });\n  Params.tableName = 'wb_key';\n  Params.db = '';//prevent inject db from params\n  Wb.sync(Params);\n  Util.loadKey(Params.keyName);\n  Wb.send({ insert });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-list.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select key list",
    "serverScript": "function main() {\n  let data, sql = 'select * from wb_key where rid={?rid?}';\n  if (Params.searchList) {\n    Wb.setLike('searchList');\n    sql += ' and map_k like {?searchList?} or map_v like {?searchList?}';\n  }\n  sql += ' order by map_k';\n  data = Wb.getDict(sql, 'wb,');\n  //sort by number\n  if (Params.keyType == '1') {\n    data.items.sort((a, b) => {\n      return parseInt(a.map_k) - parseInt(b.map_k);\n    });\n  }\n  Wb.send(data);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-type.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select key types",
    "serverScript": "function main() {\n  let sql = 'select * from wb_key_type';\n  if (Params.searchType) {\n    Wb.setLike('searchType');\n    sql += ' where key_name like {?searchType?}';\n  }\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, ['wb_key_type', 'wb']);\n}\nmain();"
  },
  "_icon": "module"
}

File name: kve.xwl


{
  "title": "@kve",
  "icon": "list-view",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Key value mapping setting"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /**\n   * Method to execute when adding or updating key type fails.\n   * @param {String} resp Response text.\n   */\n  onFailure(resp) {\n    Wb.checkExists(resp, 'wb_key_type_key_name', app.typeGrid.dictWin.down('key_name'));\n  },\n  /** @property {String} - The currently selected key name. */\n  get$keyName() {\n    return app.typeGrid.selection?.data.key_name;\n  },\n  /** @property {String} - The currently selected key name ID. */\n  get$keyId() {\n    return app.typeGrid.selection?.data.sid;\n  },\n  /** @property {String} - The currently selected key category. */\n  get$keyType() {\n    return app.typeGrid.selection?.data.key_type;\n  },\n  /**\n   * Fired after the list table data is changed. @priv\n   */\n  onChange() {\n    app.saveBtn.disabled = false;\n  },\n  /**\n   * Save the list data. @priv\n   * @param {Function} [success] The callback method after save successfully.\n   */\n  save(success) {\n    let grid = app.listGrid;\n    grid.sync({\n      url: xpath + '/save-list',\n      params: { keyId: app.keyId, keyName: app.keyName },\n      success() {\n        app.saveBtn.disabled = true;\n        success?.();\n      },\n      failure(resp) {\n        Wb.checkExists(resp, 'wb_key_unq1', Str.key);\n      }\n    });\n  }\n});",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "grid",
          "text": "typeGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "typeGrid",
            "url": "@xpath + '/select-type'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "width": "50vw",
            "autoContextMenu": "true",
            "title": "@Str.keyName",
            "sorters": "key_name",
            "maxWidth": "30em",
            "keyFields": "sid,key_name"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "this.downWhole('editBtn').fireEvent('click');",
            "selectionchange": "let data = app.typeGrid.selectionData, keyName = data?.key_name, listGrid = app.listGrid, searchList = app.searchList;\n\nlistGrid.disabled = !keyName;\nlistGrid.subTitle = keyName;\nsearchList.suspendEvent();\nsearchList.clear();\nsearchList.resumeEvent();\nif (keyName) {\n  listGrid.load();\n} else {\n  listGrid.destroyAll();\n  listGrid.commit();\n  app.saveBtn.disabled = true;\n}",
            "beforeselect": "if (!app.saveBtn.disabled) {\n  Wb.choose(Str.saveChanges.format(Str.list), btn => {\n    if (btn == 'yes') {\n      app.save(f => item.select());\n    } else if (btn == 'no') {\n      app.saveBtn.disabled = true;\n      item.select()\n    }\n  });\n  return false;\n}"
          },
          "items": [
            {
              "_icon": "pagenum",
              "text": "pagingBar",
              "cls": "Wb.PagingBar",
              "properties": {
                "cid": "pagingBar",
                "isProperty": "true",
                "showColumnsMenu": "false"
              },
              "_expanded": true,
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "app.typeGrid.dictAdd({\n  url: xpath + '/add-type',\n  failure: app.onFailure\n}, Str.keyName);"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "editBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "editBtn",
                    "text": "@Str.edit",
                    "icon": "edit",
                    "keys": "Ctrl+J"
                  },
                  "events": {
                    "click": "app.typeGrid.dictEdit({\n  url: xpath + '/edit-type',\n  failure: app.onFailure\n}, 'key_name');"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "app.typeGrid.removeRecords(xpath + '/del-types', 'key_name', null, f => app.saveBtn.disabled = true);"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "text",
                  "text": "searchType",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "searchType",
                    "clearButton": "true",
                    "placeholder": "@Str.search",
                    "flex": "1",
                    "minWidth": "5em",
                    "maxWidth": "20em"
                  },
                  "events": {
                    "change": "app.typeGrid.delayLoad({ comps: app.searchType });"
                  },
                  "hasDupCid": 0
                }
              ]
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          },
          "_expanded": true
        },
        {
          "_icon": "grid",
          "text": "listGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "listGrid",
            "url": "@xpath + '/select-list'",
            "multiSelect": "true",
            "flex": "1",
            "autoContextMenu": "true",
            "title": "@Str.list",
            "autoLoad": "false",
            "disabled": "true",
            "editable": "true",
            "keyFields": "sid"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "this.downWhole('editBtn').fireEvent('click');",
            "editing": "app.onChange();",
            "itemchange": "app.onChange();",
            "beforeload": "Wb.apply(params, { searchList: app.searchList.value, rid: app.keyId, keyType: app.keyType });\nreturn this.saveConfirm(app.saveBtn, app.save);"
          },
          "items": [
            {
              "_icon": "pagenum",
              "text": "pagingBar",
              "cls": "Wb.PagingBar",
              "properties": {
                "cid": "pagingBar",
                "isProperty": "true",
                "showColumnsMenu": "false"
              },
              "_expanded": true,
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "app.listGrid.addRecord();"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "app.listGrid.delRecords();"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "saveBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "saveBtn",
                    "icon": "save",
                    "text": "@Str.save",
                    "disabled": "true",
                    "keys": "Ctrl+S"
                  },
                  "events": {
                    "click": "app.save();"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider2",
                  "cls": "Wb.Divider",
                  "_expanded": true,
                  "properties": {
                    "cid": "divider2"
                  }
                },
                {
                  "_icon": "text",
                  "text": "searchList",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "searchList",
                    "clearButton": "true",
                    "placeholder": "@Str.search",
                    "flex": "1",
                    "minWidth": "5em",
                    "maxWidth": "20em"
                  },
                  "events": {
                    "change": "app.listGrid.delayLoad();"
                  },
                  "hasDupCid": 0
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: check-out.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Check out files",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let sqlMap = Wb.Query.getArraySql(Wb.getObject('ids'));\n\n  Wb.sql({\n    sql: 'select file_path,file_content from wb_version where sid in (' + sqlMap.sql + ')',\n    params: sqlMap.params,\n    blob: true,\n    fn(row) {\n      new Wb.File(true, row.file_path).stream = row.file_content;\n    }\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Delete version files",
    "serverScript": "function main() {\n  let del = Wb.getObject('del');\n\n  Wb.sync({ tableName: 'wb_version', del });\n}\nmain();"
  },
  "_icon": "module"
}

File name: rollback.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Rollback to the specified version",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  Wb.sql({\n    sql: 'select file_path,file_content from wb_version where file_version={?version?}',\n    blob: true,\n    fn(row) {\n      new Wb.File(true, row.file_path).stream = row.file_content;\n    }\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-version.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select all versions",
    "serverScript": "function main() {\n  Wb.sendRecords({\n    sql: 'select distinct file_version from wb_version order by file_version desc',\n    rs: -1\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select version files",
    "serverScript": "function main() {\n  let sql, where = [];\n\n  sql = 'select sid, sdate, file_path, file_version, version_remark, modify_date from wb_version';\n  if (Params.searchVersion)\n    where.push('file_version={?searchVersion?}');\n  if (Params.searchFile) {\n    Wb.setLike('searchFile');\n    where.push('file_path like {?searchFile?}');\n  }\n  if (where.length)\n    sql += ' where ' + where.join(' and ');\n  sql += Wb.getOrderSql();\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: version.xwl


{
  "title": "@versionMng",
  "icon": "form",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "File version management"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Check out files.\n   */\n  checkOut() {\n    let sels = app.grid1.selections;\n    if (!sels.length) {\n      Wb.tipSelect();\n      return;\n    }\n    Wb.confirm(Wb.getActionHint(sels, 'file_path', Str.checkOut), f => {\n      Wb.ajax({\n        url: xpath + '/check-out',\n        params: { ids: sels.map(item => item.data.sid) },\n        success() {\n          Wb.tipDone(Str.checkOut);\n        }\n      });\n    }).showBy(app.checkOutBtn, 'br');\n  },\n  /**\n   * Load grid data.\n   */\n  load() {\n    app.grid1.delayLoad({ comps: app.viewport1.tbar });\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "backWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "backWin",
        "layout": "grid1",
        "dialog": "true",
        "icon": "undo",
        "title": "@Str.back"
      },
      "events": {
        "ok": "let version = app.versionSelect.value;\nWb.confirm(Str.actionConfirm.format(Str.back, version), f => {\n  Wb.ajax({\n    url: xpath + '/rollback',\n    params: { version },\n    success() {\n      app.backWin.close();\n    }\n  });\n});"
      },
      "items": [
        {
          "_icon": "combo",
          "text": "versionSelect",
          "cls": "Wb.Select",
          "properties": {
            "cid": "versionSelect",
            "url": "@xpath + '/select-version'",
            "text": "@Str.version",
            "required": "true",
            "editable": "false"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "checkOutBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "checkOutBtn",
                "text": "@Str.checkOut",
                "icon": "logout",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.checkOut();"
              }
            },
            {
              "_icon": "item",
              "text": "backBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "backBtn",
                "text": "@Str.back",
                "icon": "undo"
              },
              "events": {
                "click": "app.backWin.show();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'file_path');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "combo",
              "text": "searchVersion",
              "cls": "Wb.Select",
              "properties": {
                "cid": "searchVersion",
                "url": "@xpath + '/select-version'",
                "placeholder": "@Str.version",
                "clearButton": "true",
                "width": "8em",
                "editable": "false"
              },
              "events": {
                "select": "app.load();",
                "clearclick": "app.load();"
              }
            },
            {
              "_icon": "text",
              "text": "searchFile",
              "cls": "Wb.Text",
              "properties": {
                "cid": "searchFile",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.load();"
              },
              "hasDupCid": 0
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "{name:'sdate', desc: true}",
            "keyFields": "sid"
          },
          "_expanded": true
        }
      ]
    }
  ]
}

File name: welcome.xwl


{
  "title": "@welcome",
  "icon": "logo",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.set('title', Wb.getConfig('sys.app.title'));",
    "remark": "Welcome page"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "html": "<div cid='lang-en' class='w-html' style='line-height:2;display:none;'>\n  <p>\n  <div>Welcome to use WebBuilder!</div>\n  <div>WebBuilder is a powerful rapid web application development and runtime platform, with WebBuilder you can\n    develop unparalleled applications.</div>\n  <div>Please follow these steps below to start WebBuilder:</div>\n  </p>\n  <ul>\n    <li>\n      <span cid='demos' class='w-key-color w-link'>Open Examples</span><br>\n      <span>Classic examples to help you get started quickly.</span>\n    </li>\n    <li>\n      <span cid='docs' class='w-key-color w-link'>Open Docs For Help</span><br>\n      <span>View development manuals and API documents.</span>\n    </li>\n    <li>\n      <span cid='ide' class='w-key-color w-link'>Open Integrated Development Environment</span><br>\n      <span>A powerful development tool for developing awesome applications.</span>\n    </li>\n    <li>\n      <span cid='dbc' class='w-key-color w-link'>Open Database Configuration</span><br>\n      <span>Add your databases and click the [Default] button to set it as the default database.</span>\n    </li>\n    <li>\n      <span cid='user' class='w-key-color w-link'>Open User Management</span><br>\n      <span>Add users and change your password in time.</span>\n    </li>\n  </ul>\n  <p>\n  <div>If you have any questions please contact: <a class='w-key-color w-link'\n      href='mailto:contact@geejing.com'>contact@geejing.com</a></div>\n  <div>Download update: <a class='w-key-color w-link' href=\"https://www.geejing.com/download\"\n      target=\"_blank\">https://www.geejing.com/download</a>\n  </div>\n  <div>For more information please visit: <a class='w-key-color w-link' href=\"https://www.geejing.com\"\n      target=\"_blank\">https://www.geejing.com</a>\n  </div>\n  </p>\n</div>\n<div cid='lang-zh-cn' class='w-html' style='line-height:2;display:none;'>\n  <p>\n  <div>欢迎使用 WebBuilder!</div>\n  <div>WebBuilder 是一款强大的快速Web应用开发和运行平台,使用 WebBuilder 您可以开发出无与伦比的应用系统。</div>\n  <div>请按下列步骤开始使用 WebBuilder:</div>\n  </p>\n  <ul>\n    <li>\n      <span cid='demos' class='w-key-color w-link'>打开示例</span><br>\n      <span>一些经典的示例帮助您快速学习和掌握。</span>\n    </li>\n    <li>\n      <span cid='docs' class='w-key-color w-link'>打开文档</span><br>\n      <span>查看开发手册和API文档。</span>\n    </li>\n    <li>\n      <span cid='ide' class='w-key-color w-link'>打开集成开发环境</span><br>\n      <span>用于开发应用系统的强大开发工具。</span>\n    </li>\n    <li>\n      <span cid='dbc' class='w-key-color w-link'>打开数据库配置</span><br>\n      <span>添加您的数据库,并点击[默认]按钮,设置该数据库为默认数据库。</span>\n    </li>\n    <li>\n      <span cid='user' class='w-key-color w-link'>打开用户管理</span><br>\n      <span>添加用户并及时修改您的密码。</span>\n    </li>\n  </ul>\n  <p>\n  <div>如果您有任何问题请联系:<a class='w-key-color w-link' href='mailto:contact@geejing.com'>contact@geejing.com</a></div>\n  <div>下载更新:<a class='w-key-color w-link' href=\"https://www.geejing.com/download\"\n      target=\"_blank\">https://www.geejing.com/download</a>\n  </div>\n  <div>更多信息请访问:<a class='w-key-color w-link' href=\"https://www.geejing.com\" target=\"_blank\">https://www.geejing.com</a>\n  </div>\n  </p>\n</div>",
        "autoScroll": "true"
      },
      "_expanded": true,
      "events": {
        "click": "let node;\nswitch (e.target.getAttribute('cid')) {\n  case 'docs':\n    Wb.open('m?xwl=dev/docs');\n    break;\n  case 'dbc':\n    Wb.open('m?xwl=admin/dbc');\n    break;\n  case 'user':\n    Wb.open('m?xwl=admin/user');\n    break;\n  case 'ide':\n    Wb.openWin({ url: 'ide', params: { openExample: 1 } });\n    break;\n  case 'demos':\n    if (parentApp?.inHome)\n      node = parentApp.moduleTree.find(node => node.text == Str.example);\n    else if (parentApp?.inIDE)\n      node = parentApp.moduleNode.find(node => node.text == 'example');\n    if (node) {\n      node.expand();\n      node.selected = true;\n      node.highlight();\n    } else {\n      Wb.openWin({ url: 'ide', params: { openExample: 1 } });\n    }\n    break;\n}",
        "ready": "let el = this.el, div;\nif (Str.lang == 'zh-cn')\n  div = el.query('[cid=lang-zh-cn]');\nelse\n  div = el.query('[cid=lang-en]');\ndiv.setStyle('display', 'block');"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "bbar",
            "isProperty": "true",
            "padding": "true"
          },
          "text": "bbar",
          "_expanded": true,
          "_icon": "toolbar",
          "items": [
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "fill1"
              }
            },
            {
              "_icon": "label",
              "text": "copyrightLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "copyrightLabel",
                "text": "Copyright © Geejing, All Rights Reserved.",
                "cls": "w-sub-color",
                "fontSize": ".8em"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: home.xwl


{
  "title": "Office Home",
  "icon": "home",
  "img": "",
  "tags": "{newWin: true}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Params.oaPath = Wb.encode(Base.pathText + 'wb/modules/example/apps/oa');\n}\nmain();"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Open the specified module.\n   * @param {Object} data Configs data.\n   * @param {Boolean} [reload] Whether reload module.\n   */\n  openModule(data, reload) {\n    let configs, url = 'm?xwl=' + data.path;\n\n    configs = Wb.apply({ url }, Wb.parse(data.tags));\n    if (configs.frame)\n      Wb.browse(configs);\n    else if (configs.newWin)\n      Wb.openWin(configs);\n    else {\n      let ct = app.clientCardCt, card;\n\n      card = ct.find(card => card.moduleUrl == url);\n      if (card) {\n        if (reload) {\n          card.destroy();\n          card = null;\n        } else {\n          app.clientCardCt.activeCard = card;\n        }\n      }\n      if (!card) {\n        let modulePanel = app.clientCardCt.add({ cname: 'container', moduleUrl: url, moduleData: data });\n        app.clientCardCt.activeCard = modulePanel;\n        configs.container = modulePanel;\n        Wb.run(configs);\n      }\n    }\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "row",
        "border": "false"
      },
      "items": [
        {
          "_icon": "array",
          "text": "tbar",
          "cls": "Wb.Array",
          "properties": {
            "cid": "tbar"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "bannerCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "bannerCt",
                "height": "50",
                "border": "false",
                "layout": "row",
                "align": "center",
                "gap": "20px",
                "padding": "0 15px 0 15px",
                "textSelectable": "false",
                "justify": "space-between",
                "cls": "w-bar-color",
                "background": "url(wb/images/app/banner1.png;) no-repeat 0 0 / 100% 100%"
              },
              "_expanded": false,
              "items": [
                {
                  "_icon": "container",
                  "text": "leftCt",
                  "cls": "Wb.Container",
                  "_expanded": false,
                  "properties": {
                    "cid": "leftCt",
                    "layout": "row",
                    "align": "center"
                  },
                  "items": [
                    {
                      "_icon": "logo",
                      "text": "logoIcon",
                      "cls": "Wb.Icon",
                      "_expanded": true,
                      "properties": {
                        "cid": "logoIcon",
                        "icon": "flower",
                        "fontSize": "38px"
                      }
                    },
                    {
                      "_icon": "component",
                      "text": "titleComp",
                      "cls": "Wb.Component",
                      "_expanded": true,
                      "properties": {
                        "cid": "titleComp",
                        "html": "<span style=\"color:#fff;font-size:32px;font-weight:bold;font-style:italic;padding:.5em\">My Office System</span>"
                      }
                    }
                  ]
                },
                {
                  "_icon": "container",
                  "text": "centerCt",
                  "cls": "Wb.Container",
                  "_expanded": false,
                  "properties": {
                    "cid": "centerCt"
                  }
                },
                {
                  "_icon": "container",
                  "text": "rightCt",
                  "cls": "Wb.Container",
                  "_expanded": true,
                  "properties": {
                    "cid": "rightCt"
                  },
                  "items": [
                    {
                      "_icon": "button",
                      "text": "menuBtn",
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "menuBtn",
                        "icon": "menu",
                        "type": "tool"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "cls": "Wb.Menu",
                          "properties": {
                            "cid": "menu",
                            "isProperty": "true"
                          },
                          "text": "menu",
                          "_expanded": true,
                          "_icon": "menu2",
                          "hasDupCid": 0,
                          "items": [
                            {
                              "_icon": "item",
                              "text": "logoutItem",
                              "cls": "Wb.Item",
                              "_expanded": true,
                              "properties": {
                                "cid": "logoutItem",
                                "text": "@Str.logout",
                                "icon": "power"
                              },
                              "events": {
                                "click": "// Wb.ajax({\n//   url: 'logout'\n// });"
                              }
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "container",
              "text": "addressCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "addressCt",
                "layout": "row",
                "padding": "true",
                "align": "stretch",
                "gap": ".2em"
              },
              "_expanded": false,
              "items": [
                {
                  "_icon": "text",
                  "text": "urlText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "urlText",
                    "readonly": "true",
                    "text": "URL",
                    "flex": "1",
                    "selectOnFocus": "true"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "button",
                  "text": "refreshItem",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "refreshItem",
                    "icon": "refresh",
                    "tip": "@Str.refresh",
                    "type": "tool"
                  },
                  "events": {
                    "click": "let moduleData = app.clientCardCt.activeCard?.moduleData;\nif (moduleData) {\n  app.openModule(moduleData, true);\n}"
                  }
                },
                {
                  "_icon": "button",
                  "text": "closeItem",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "closeItem",
                    "icon": "delete",
                    "tip": "@Str.close",
                    "type": "tool"
                  },
                  "events": {
                    "click": "app.clientCardCt.activeCard?.destroy();"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "moduleTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "moduleTree",
            "url": "m?xwl=sys/portal/home/get-modules-tree",
            "cls": "w-home-tree",
            "treeStyle": "menu",
            "dblclickExpand": "false",
            "width": "18em",
            "rightExpander": "true"
          },
          "events": {
            "beforeload": "let node = configs.node;\n\n// params.path = node?.data.path || _$oaPath$_;\nparams.path = node?.data.path;",
            "itemclick": "if (item.leaf)\n  app.openModule(item.data);\nelse\n  item.toggleExpand();"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "_expanded": true,
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "win-restore",
          "text": "clientCardCt",
          "cls": "Wb.CardCt",
          "properties": {
            "cid": "clientCardCt",
            "flex": "1",
            "useHash": "true"
          },
          "events": {
            "cardchange": "let url = newCard?.moduleUrl;\napp.urlText.value = url ? Wb.qualifyURL(url) : '';"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "mainCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "mainCt",
                "background": "true",
                "gap": "1em",
                "padding": "1em",
                "layout": "column",
                "shrink": "false",
                "autoScroll": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "container",
                  "text": "searchCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "searchCt",
                    "layout": "row",
                    "gap": ".8em",
                    "padding": "true",
                    "background": "false",
                    "align": "center",
                    "roundBorder": "true"
                  },
                  "_expanded": false,
                  "items": [
                    {
                      "_icon": "logo",
                      "text": "icon1",
                      "cls": "Wb.Icon",
                      "properties": {
                        "cid": "icon1",
                        "icon": "calendar"
                      }
                    },
                    {
                      "_icon": "button",
                      "text": "today",
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "today",
                        "text": "Today",
                        "groupName": "dateButtons",
                        "active": "true",
                        "shape": "round",
                        "border": "false"
                      },
                      "_expanded": true
                    },
                    {
                      "_icon": "button",
                      "text": "week",
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "week",
                        "text": "This Week",
                        "groupName": "dateButtons",
                        "shape": "round",
                        "border": "false"
                      },
                      "_expanded": true
                    },
                    {
                      "_icon": "button",
                      "text": "month",
                      "cls": "Wb.Button",
                      "_expanded": true,
                      "properties": {
                        "cid": "month",
                        "text": "This Month",
                        "groupName": "dateButtons",
                        "shape": "round",
                        "border": "false"
                      }
                    },
                    {
                      "_icon": "button",
                      "text": "year",
                      "cls": "Wb.Button",
                      "_expanded": true,
                      "properties": {
                        "cid": "year",
                        "text": "This Year",
                        "groupName": "dateButtons",
                        "shape": "round",
                        "border": "false"
                      }
                    },
                    {
                      "_icon": "calendar",
                      "text": "date1",
                      "cls": "Wb.Date",
                      "properties": {
                        "cid": "date1",
                        "placeholder": "Select Date"
                      },
                      "_expanded": true
                    }
                  ]
                },
                {
                  "_icon": "container",
                  "text": "summaryCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "summaryCt",
                    "layout": "grid4",
                    "padding": "false",
                    "textSelectable": "false",
                    "color": "#555"
                  },
                  "_expanded": false,
                  "items": [
                    {
                      "_icon": "component",
                      "text": "data1",
                      "cls": "Wb.Component",
                      "properties": {
                        "cid": "data1",
                        "html": "<div class='w-row w-align-center w-justify-around w-padding1' style='background:#c0f9cb'>\n  <div class='w-column w-gap2'>\n    <div><span class='w-bold w-size1d5'>9.5%</span> Increase</div>\n    <div><span class='w-bold w-size1d5'>387</span> Amount</div>\n  </div>\n  <div class='w-icon icon-table w-size4'></div>\n</div>",
                        "frame": "true"
                      },
                      "_expanded": true
                    },
                    {
                      "_icon": "component",
                      "text": "data2",
                      "cls": "Wb.Component",
                      "properties": {
                        "cid": "data2",
                        "html": "<div class='w-row w-align-center w-justify-around w-padding1' style='background:#fde0f8'>\n  <div class='w-column w-gap2'>\n    <div><span class='w-bold w-size1d5'>$3,259</span> Total</div>\n    <div><span class='w-bold w-size1d5'>2,328</span> Visits</div>\n  </div>\n  <div class='w-icon icon-dt-picker w-size4'></div>\n</div>",
                        "frame": "true"
                      }
                    },
                    {
                      "_icon": "component",
                      "text": "data3",
                      "cls": "Wb.Component",
                      "properties": {
                        "cid": "data3",
                        "html": "<div class='w-row w-align-center w-justify-around w-padding1' style='background:#f6efbd'>\n  <div class='w-column w-gap2'>\n    <div><span class='w-bold w-size1d5'>165</span> Items</div>\n    <div><span class='w-bold w-size1d5'>5.2%</span> Percent</div>\n  </div>\n  <div class='w-icon icon-list w-size4'></div>\n</div>",
                        "frame": "true"
                      }
                    },
                    {
                      "_icon": "component",
                      "text": "data4",
                      "cls": "Wb.Component",
                      "properties": {
                        "cid": "data4",
                        "html": "<div class='w-row w-align-center w-justify-around w-padding1' style='background:#f1fffd'>\n  <div class='w-column w-gap2'>\n    <div><span class='w-bold w-size1d5'>629</span> Users</div>\n    <div><span class='w-bold w-size1d5'>9,185</span> Count</div>\n  </div>\n  <div class='w-icon icon-cluster w-size4'></div>\n</div>",
                        "frame": "true"
                      }
                    }
                  ]
                },
                {
                  "_icon": "container",
                  "text": "chartCt",
                  "cls": "Wb.Container",
                  "_expanded": false,
                  "properties": {
                    "cid": "chartCt",
                    "layout": "grid2",
                    "padding": "false",
                    "frame": "true",
                    "background": "false"
                  },
                  "items": [
                    {
                      "_icon": "chart-bar",
                      "text": "chart1",
                      "cls": "Wb.Chart",
                      "properties": {
                        "cid": "chart1",
                        "option": "({\n  legend: false,\n  grid: {\n    left: 20,\n    right: 20,\n    bottom: 20,\n    top: 20,\n    containLabel: true\n  },\n  xAxis: [\n    {\n      type: 'category',\n      boundaryGap: false,\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    }\n  ],\n  yAxis: [\n    {\n      type: 'value'\n    }\n  ],\n  series: [\n    {\n      name: 'Email',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 90, 230, 210]\n    },\n    {\n      name: 'Union Ads',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 182, 191, 234, 290, 330, 310]\n    },\n    {\n      name: 'Video Ads',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [150, 232, 201, 154, 190, 330, 410]\n    },\n    {\n      name: 'Direct',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [320, 332, 301, 334, 390, 330, 320]\n    },\n    {\n      name: 'Search Engine',\n      type: 'line',\n      stack: 'Total',\n      label: {\n        show: true,\n        position: 'top'\n      },\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [820, 932, 901, 934, 1290, 1330, 1320]\n    }\n  ]\n})",
                        "height": "25em",
                        "roundBorder": "true"
                      }
                    },
                    {
                      "_icon": "chart-bar",
                      "text": "chart2",
                      "cls": "Wb.Chart",
                      "properties": {
                        "cid": "chart2",
                        "option": "({\n  legend: false,\n  grid: {\n    left: 20,\n    right: 20,\n    bottom: 20,\n    top: 20,\n    containLabel: true\n  },\n  xAxis: [\n    {\n      type: 'category',\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    }\n  ],\n  yAxis: [\n    {\n      type: 'value'\n    }\n  ],\n  series: [\n    {\n      name: 'Direct',\n      type: 'bar',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [320, 332, 301, 334, 390, 330, 320]\n    },\n    {\n      name: 'Email',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 90, 230, 210]\n    },\n    {\n      name: 'Union Ads',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 182, 191, 234, 290, 330, 310]\n    },\n    {\n      name: 'Video Ads',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [150, 232, 201, 154, 190, 330, 410]\n    },\n    {\n      name: 'Search Engine',\n      type: 'bar',\n      data: [862, 1018, 964, 1026, 1679, 1600, 1570],\n      emphasis: {\n        focus: 'series'\n      },\n      markLine: {\n        lineStyle: {\n          type: 'dashed'\n        },\n        data: [[{ type: 'min' }, { type: 'max' }]]\n      }\n    },\n    {\n      name: 'Baidu',\n      type: 'bar',\n      barWidth: 5,\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [620, 732, 701, 734, 1090, 1130, 1120]\n    },\n    {\n      name: 'Google',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 290, 230, 220]\n    },\n    {\n      name: 'Bing',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [60, 72, 71, 74, 190, 130, 110]\n    },\n    {\n      name: 'Others',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [62, 82, 91, 84, 109, 110, 120]\n    }\n  ]\n})",
                        "height": "25em",
                        "roundBorder": "true"
                      }
                    }
                  ]
                },
                {
                  "_icon": "icon-list",
                  "text": "iconView",
                  "cls": "Wb.IconView",
                  "properties": {
                    "cid": "iconView",
                    "data": "[\n    { icon: 'user', text: 'User Management' },\n    { icon: 'support', text: 'Support Service' },\n    { icon: 'list', text: 'Consumption List' },\n    { icon: 'preview', text: 'Disabled Item', _disabled: true, _tip: 'This item is disabled' },\n    { icon: 'gear', text: 'System Settings' },\n    { icon: 'truck', text: 'The text content of this item is too long and maybe overflowed' },\n    { icon: 'database', text: 'Database Management' },\n    { icon: 'flow1', text: 'WorkFlow Design' }\n  ]",
                    "iconField": "icon",
                    "selectColor": "active",
                    "defaultFields": "({\n  badge: { value: 99 }\n})",
                    "layout": "grid4",
                    "padding": ".5em",
                    "gap": "1em",
                    "roundBorder": "true"
                  },
                  "events": {
                    "itemclick": "Wb.tip(item.data.text + ' clicked');"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let flow, rec;\n\n  flow = new Wb.Workflow({ file: 'example/leave.flw', title: Params.title });\n  flow.start();\n  rec = { sid: Wb.getId(), flow_id: flow.flowId, user_id: Wb.userid };\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_leave', insert: Params });\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n\n  Params.flowId = Wb.getObject('del').$flow_id;\n  Wb.startTrans();\n  Util.checkProcess();\n  Wb.sql('delete from wb_flow where sid={?flowId?}');\n  Wb.sql('delete from wb_flow_user where flow_id={?flowId?}');\n  Wb.sql('delete from wb_leave where flow_id={?flowId?}');\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let flowId = Params.$flow_id, title;\n\n  Params.flowId = flowId;\n  Wb.startTrans();\n  Util.checkProcess();\n  title = Params.title;\n  if (title)\n    Wb.sync({ tableName: 'wb_flow', update: { $sid: flowId, title } });\n  Wb.sync({ tableName: 'wb_leave', update: Params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let sql;\n\n  sql = 'select a.*,b.title,b.node_text from wb_leave a, wb_flow b where a.flow_id=b.sid and a.user_id={?sys.userid?}';\n  sql += Wb.getOrderSql({ title: 'b.title', node_text: 'b.node_text' });\n  Wb.sendRowx(sql);\n}\nmain();"
  },
  "_icon": "module"
}

File name: leave.xwl


{
  "title": "Leave Application",
  "icon": "paper-plane",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "layout": "grid1",
        "title": "Leave application",
        "icon": "paper-plane",
        "autoScroll": "true",
        "resetDialog": "true"
      },
      "items": [
        {
          "_icon": "text",
          "text": "title",
          "cls": "Wb.Text",
          "properties": {
            "cid": "title",
            "text": "Title",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "begin_date",
          "cls": "Wb.Date",
          "_expanded": true,
          "properties": {
            "cid": "begin_date",
            "text": "Begin Date",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "end_date",
          "cls": "Wb.Date",
          "_expanded": true,
          "properties": {
            "cid": "end_date",
            "text": "End Date",
            "required": "true"
          }
        },
        {
          "_icon": "number-edit",
          "text": "leave_days",
          "cls": "Wb.Number",
          "properties": {
            "cid": "leave_days",
            "decimalCount": "0",
            "text": "Leave days",
            "required": "true",
            "minValue": "0",
            "maxLength": "5"
          }
        },
        {
          "_icon": "text",
          "text": "leave_reason",
          "cls": "Wb.Text",
          "properties": {
            "cid": "leave_reason",
            "text": "Reason",
            "required": "true"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.grid1.insertRecord(xpath + '/add', app.window1);"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "app.grid1.editRecord(xpath + '/edit', app.window1);"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'title');"
              }
            }
          ]
        },
        {
          "_icon": "label1",
          "text": "bbar",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "bbar",
            "icon": "info1",
            "isProperty": "true",
            "value": "This module demonstrates workflow and multi-tables CRUD",
            "padding": "true"
          },
          "hasDupCid": 0
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "sorters": "{name: 'begin_date', desc: true}",
            "url": "@xpath + '/select'",
            "columnsSortable": "true"
          },
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "beginDateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "beginDateCol",
                    "text": "Begin Date",
                    "fieldName": "begin_date",
                    "type": "dateText"
                  }
                },
                {
                  "_icon": "column",
                  "text": "endDateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "endDateCol",
                    "text": "End Date",
                    "fieldName": "end_date",
                    "type": "dateText"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "column",
                  "text": "titleCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "titleCol",
                    "text": "Title",
                    "fieldName": "title",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "leaveDaysCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "leaveDaysCol",
                    "text": "Leave Days",
                    "fieldName": "leave_days"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "column",
                  "text": "approvalNodeCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "approvalNodeCol",
                    "text": "Approval Node",
                    "fieldName": "node_text"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "column",
                  "text": "leaveReasonCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "leaveReasonCol",
                    "text": "Leave Reason",
                    "fieldName": "leave_reason",
                    "width": "-1",
                    "minWidth": "8em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "actionsCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "actionsCol",
                    "width": "3em",
                    "freeze": "true"
                  },
                  "items": [
                    {
                      "cls": "Wb.Array",
                      "properties": {
                        "cid": "buttons"
                      },
                      "text": "buttons",
                      "_expanded": true,
                      "_icon": "array",
                      "items": [
                        {
                          "_icon": "item",
                          "text": "withdrawItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "withdrawItem",
                            "icon": "undo",
                            "tip": "Withdraw Application"
                          },
                          "events": {
                            "click": "this.gridItem.select();\napp.grid1.removeRecords(xpath + '/del', 'title');"
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let rec = { sid: Wb.getId() };\n\n  Wb.set(rec);\n  Wb.sync({ tableName: 'wb_staff', insert: Params });\n  Wb.send(rec);\n}\nmain();"
  },
  "_icon": "module"
}

File name: del.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let del = Wb.getObject('del');\n\n  Wb.sync({ tableName: 'wb_staff', del });\n}\nmain();"
  },
  "_icon": "module"
}

File name: edit.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.sync({ tableName: 'wb_staff', update: Params });\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-photo.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let row = Wb.getRow({\n    sql: 'select photo from wb_staff where sid={?sid?}',\n    blob: true\n  });\n  if (row?.photo)\n    Wb.exportData(row.photo);\n  else\n    Wb.exportData(new Wb.File(true, 'wb/images/null.png').stream);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select-dept.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let firstItem, sql, rows, isRoot;\n\n  // whether is root node\n  isRoot = !Params.sid;\n  if (isRoot) {\n    Params.sid = '0';\n  };\n  sql = `\n        select a.sid, a.dept_code, a.dept_name, 'folder1' as \"_icon\",\n        case when (select count(*) from wb_dept b where b.parent_id=a.sid)>0 then 1 else 0 end as items\n        from wb_dept a where a.status=1 and a.parent_id={?sid?} order by a.dept_code\n        `;\n  rows = Wb.getAllRows(sql);\n  if (isRoot) {\n    if ((firstItem = rows[0]) && rows.length == 1)\n      firstItem._expanded = true;\n    rows.insert(0, { dept_name: Str.all, _icon: 'list1', _leaf: true });\n  }\n  Wb.send(rows);\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let download = Wb.getObject('download');\n  if (download) {\n    //download blob\n    let row = Wb.getRow({\n      sql: 'select full_name, photo from wb_staff where sid={?$sid?}',\n      blob: true,\n      params: download\n    });\n    if (row?.photo)\n      Wb.exportData(row.photo, row.full_name + '.png');\n    else\n      Wb.raise('Photo not found.');\n  } else {\n    // select rows\n    // getOrderSql will not cause SQL injection\n    let sql;\n\n    sql = `\n        select a.*,b.user_name,c.dept_name,c.dept_code from wb_staff a, wb_user b, wb_dept c\n        where a.user_id=b.sid and a.dept_id=c.sid\n        `;\n    if (Params.searchText) {\n      Wb.setLike('searchText');\n      sql += ' and (a.full_name like {?searchText?} or a.code like {?searchText?})';\n    } else if (Params.searchDeptId) {\n      sql += ' and a.dept_id={?searchDeptId?}';\n    }\n    sql += Wb.getOrderSql({ email: 'a.email' });// There are 2 emails fields in the tables\n    Wb.sendRowx({ sql, kv: { gender: 'gender' } }); // or Wb.sendRowx({ sql, kv: 'gender' } });\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: person.xwl


{
  "title": "Person Management",
  "icon": "user3",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "editWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "editWin",
        "layout": "column",
        "resetDialog": "true",
        "width": "60em",
        "gap": "true",
        "padding": "true",
        "autoScroll": "true"
      },
      "items": [
        {
          "_icon": "fieldset",
          "text": "generalFieldSet",
          "cls": "Wb.Fieldset",
          "properties": {
            "cid": "generalFieldSet",
            "title": "General Information",
            "layout": "grid2",
            "collapsible": "true",
            "icon": "address-book",
            "autoGrid": "true"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Text",
              "properties": {
                "cid": "code",
                "required": "true",
                "maxLength": "5",
                "text": "Code"
              },
              "text": "code",
              "_expanded": true,
              "_icon": "text"
            },
            {
              "_icon": "search-file",
              "text": "photo",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "photo",
                "accept": ".png",
                "text": "Photo",
                "gridRow": "span 3",
                "browseMode": "true",
                "browseHeight": "8em"
              }
            },
            {
              "cls": "Wb.Text",
              "properties": {
                "cid": "full_name",
                "required": "true",
                "text": "Full name"
              },
              "text": "full_name",
              "_expanded": true,
              "_icon": "text"
            },
            {
              "_icon": "combo",
              "text": "user_name",
              "cls": "Wb.Select",
              "properties": {
                "cid": "user_name",
                "textField": "user_name",
                "valueField": "sid",
                "url": "m?xwl=sys/dialog/user-selector/select",
                "required": "true",
                "forceSelect": "true",
                "text": "Username",
                "bindField": "user_id",
                "subtextField": "display_name"
              }
            },
            {
              "_icon": "combo",
              "text": "dept_name",
              "cls": "Wb.Select",
              "properties": {
                "cid": "dept_name",
                "url": "m?xwl=sys/dialog/dept-selector/select",
                "textField": "dept_name",
                "valueField": "sid",
                "clearButton": "true",
                "treePicker": "true",
                "subtextField": "dept_code",
                "required": "true",
                "text": "Dept name",
                "gridColumn": "span 2",
                "valueMap": "{ sid: 'dept_id', dept_name: 'dept_name', dept_code: 'dept_code' }"
              }
            },
            {
              "_icon": "calendar",
              "text": "birth_date",
              "cls": "Wb.Date",
              "properties": {
                "cid": "birth_date",
                "text": "Birth date"
              }
            },
            {
              "_icon": "combo",
              "text": "gender",
              "cls": "Wb.Select",
              "properties": {
                "cid": "gender",
                "text": "Gender",
                "keyName": "gender"
              }
            },
            {
              "_icon": "number-edit",
              "text": "height",
              "cls": "Wb.Number",
              "properties": {
                "cid": "height",
                "minValue": "0",
                "maxValue": "999",
                "decimalCount": "0",
                "text": "Height"
              }
            },
            {
              "cls": "Wb.Text",
              "properties": {
                "cid": "email",
                "valueType": "email",
                "text": "Email"
              },
              "text": "email",
              "_expanded": true,
              "_icon": "text"
            },
            {
              "_icon": "number-edit",
              "text": "salary",
              "cls": "Wb.Number",
              "properties": {
                "cid": "salary",
                "decimalCount": "2",
                "text": "Salary"
              }
            }
          ]
        },
        {
          "_icon": "fieldset",
          "text": "resumeFieldSet",
          "cls": "Wb.Fieldset",
          "_expanded": true,
          "properties": {
            "cid": "resumeFieldSet",
            "layout": "fit",
            "title": "Personal Resume",
            "collapsible": "true",
            "icon": "text1",
            "flex": "1"
          },
          "items": [
            {
              "_icon": "textarea",
              "text": "cv",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "cv",
                "minHeight": "10em"
              }
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "let deptData;\n\napp.grid1.insertRecord({\n  url: xpath + '/add',\n  failure(resp) {\n    Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n  }\n}, app.editWin);\n// set default dept\ndeptData = app.tree1.selectionData;\nif (deptData?.sid)\n  app.dept_name.value = Wb.applyWith({}, deptData, 'sid,dept_code,dept_name');"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let rec = app.grid1.selection;\napp.grid1.editRecord({\n  url: xpath + '/edit',\n  failure(resp) {\n    Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n  }\n}, app.editWin);\nif (rec)\n  app.editWin.down('photo').preview(xpath + '/get-photo&sid=' + rec.data.sid + '&' + Wb.getId());"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/del', 'code');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              },
              "_expanded": true
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "let params;\n\nif (value)\n  params = { searchText: value };\nelse\n  params = { searchDeptId: app.tree1.selectionData?.sid };\napp.grid1.delayLoad({ params });"
              }
            }
          ]
        },
        {
          "_icon": "label1",
          "text": "bbar",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "bbar",
            "icon": "info1",
            "isProperty": "true",
            "value": "This module demonstrates database CRUD",
            "padding": "true"
          },
          "hasDupCid": 0
        },
        {
          "_icon": "tree-view",
          "text": "tree1",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "tree1",
            "width": "18em",
            "url": "@xpath + '/select-dept'",
            "textField": "dept_name",
            "autoSelect": "true"
          },
          "events": {
            "selectionchange": "app.grid1.load({ params: { searchDeptId: app.tree1.selectionData?.sid } });"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "code",
            "stateId": "wb.oa.person",
            "autoLoad": "false",
            "flex": "1"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "codeCol",
                    "fieldName": "code",
                    "text": "Code",
                    "width": "5em"
                  },
                  "text": "codeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fullNameCol",
                    "fieldName": "full_name",
                    "text": "Full name",
                    "width": "13em"
                  },
                  "text": "fullNameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "usernameCol",
                    "fieldName": "user_name",
                    "text": "Username"
                  },
                  "text": "usernameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "deptCol",
                    "fieldName": "dept_name",
                    "text": "Department",
                    "render": "Wb.Column.addSubText(el, value, data.dept_code);",
                    "width": "13em"
                  },
                  "text": "deptCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "birthDateCol",
                    "fieldName": "birth_date",
                    "text": "Birth date"
                  },
                  "text": "birthDateCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "genderCol",
                    "fieldName": "gender",
                    "text": "Gender",
                    "width": "6em",
                    "keyValue": "true"
                  },
                  "text": "genderCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "heightCol",
                    "fieldName": "height",
                    "text": "Height",
                    "width": "6em"
                  },
                  "text": "heightCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "emailCol",
                    "fieldName": "email",
                    "text": "Email",
                    "width": "18em"
                  },
                  "text": "emailCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "salaryCol",
                    "fieldName": "salary",
                    "text": "Salary",
                    "type": "usd",
                    "width": "9em"
                  },
                  "text": "salaryCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "cvCol",
                    "fieldName": "cv",
                    "text": "CV",
                    "width": "15em",
                    "sortable": "false"
                  },
                  "text": "cvCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "photoCol",
                    "fieldName": "photo",
                    "text": "Photo",
                    "width": "15em",
                    "render": "if (value)\n  Wb.Column.createDownload(el, column.fieldName, data.full_name + '.png');",
                    "sortable": "false"
                  },
                  "text": "photoCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: add-tab-card.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "card",
      "text": "cardTpl",
      "cls": "Wb.Card",
      "_expanded": true,
      "properties": {
        "cid": "cardTpl",
        "layout": "grid1",
        "instanced": "false"
      },
      "items": [
        {
          "_icon": "text",
          "text": "text1",
          "cls": "Wb.Text",
          "properties": {
            "cid": "text1",
            "text": "Text"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number1",
          "cls": "Wb.Number",
          "properties": {
            "cid": "number1",
            "text": "Number"
          }
        }
      ]
    },
    {
      "_icon": "tab",
      "text": "tab1",
      "cls": "Wb.Tab",
      "_expanded": true,
      "properties": {
        "cid": "tab1",
        "full": "true"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addNewCardItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "addNewCardItem",
                "text": "Add New Card",
                "icon": "card"
              },
              "events": {
                "click": "let newCard = app.cardTpl;\n\n//app.cardObject ??= app.cardTpl;\nnewCard.cid = Wb.getId(); // let card cid unique\napp.cardIndex ??= 0;\napp.cardIndex++;\nnewCard.title = 'Card ' + app.cardIndex;\nnewCard = app.tab1.add(app.cardTpl);\nnewCard.down('text1').value = 'text ' + app.cardIndex;\nnewCard.show();"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: create-page.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Create Page",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "label",
          "text": "descLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "descLabel",
            "text": "This example demonstrates how to create pages"
          }
        },
        {
          "_icon": "title",
          "text": "staticPageTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "staticPageTitle",
            "title": "Static page"
          }
        },
        {
          "_icon": "label",
          "text": "staticPageLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "staticPageLabel",
            "text": "Create a page by defining HTML property"
          }
        },
        {
          "_icon": "panel",
          "text": "staticPagePanel",
          "cls": "Wb.Panel",
          "_expanded": true,
          "properties": {
            "cid": "staticPagePanel",
            "html": "<div class=\"w-grid2\" style=\"grid-template-columns: 8em 1fr;\">\n  <div>Code:</div>\n  <div>10001</div>\n  <div>Name:</div>\n  <div>Terri Duffy</div>\n  <div>Birth Date:</div>\n  <div>2002/07/01</div>\n  <div>Email:</div>\n  <div>terri0@geejing.com</div>\n</div>"
          }
        },
        {
          "_icon": "title",
          "text": "dynamicPageTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dynamicPageTitle",
            "title": "Dynamic page"
          }
        },
        {
          "_icon": "label",
          "text": "dynamicPageLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "dynamicPageLabel",
            "text": "Create a page by set HTML and create elements in ready event"
          }
        },
        {
          "_icon": "panel",
          "text": "dynamicPagePanel",
          "cls": "Wb.Panel",
          "_expanded": false,
          "properties": {
            "cid": "dynamicPagePanel",
            "html": "<div class=\"w-padding\">\n  <div cid=\"title\" class=\"w-title2\"></div>\n  <div cid=\"subTitle\"></div>\n  <div cid=\"list\" class=\"w-grid2\" style=\"grid-template-columns: 8em 1fr;\">\n  </div>\n</div>"
          },
          "events": {
            "ready": "let el = this.el, listEl;\n\n// use textContent\nel.query('[cid=title]').textContent = 'Staff form';\n// use innerHTML\nel.query('[cid=subTitle]').innerHTML = '<span>' + new Date().dateText + '</span>';\n// dynamic create element (Suggested)\nlistEl = el.query('[cid=list]');\nWb.ajax({\n  url: 'demo-source?xaction=staffFirstRow',\n  json: true,\n  success(resp) {\n    listEl.addEl().textContent = 'Code:';\n    listEl.addEl().textContent = resp.code;\n    listEl.addEl().textContent = 'Name:';\n    listEl.addEl().textContent = resp.full_name;\n    listEl.addEl().textContent = 'Birth Date:';\n    listEl.addEl().textContent = resp.birth_date.dateValue.dateText;\n    listEl.addEl().textContent = 'Email:';\n    listEl.addEl().textContent = resp.email;\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "tplPageTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tplPageTitle",
            "title": "Template page"
          }
        },
        {
          "_icon": "label",
          "text": "tplPageLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "tplPageLabel",
            "text": "Create a page by defining tpl and update data in ready event"
          }
        },
        {
          "_icon": "panel",
          "text": "tplPagePanel",
          "cls": "Wb.Panel",
          "_expanded": true,
          "properties": {
            "cid": "tplPagePanel",
            "tpl": "<div class=\"w-grid2\" style=\"grid-template-columns: 8em 1fr;\">\n  <div>Code:</div>\n  <div>{code}</div>\n  <div>Name:</div>\n  <div>{full_name}</div>\n  <div>Birth Date:</div>\n  <div>{birth_date}</div>\n  <div>Email:</div>\n  <div>{email}</div>\n</div>"
          },
          "events": {
            "ready": "Wb.ajax({\n  url: 'demo-source?xaction=staffFirstRow',\n  json: true,\n  success(data) {\n    app.tplPagePanel.update(data);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "compPageTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "compPageTitle",
            "title": "Component page"
          }
        },
        {
          "_icon": "label",
          "text": "compPageLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "compPageLabel",
            "text": "Create a page by adding components in ready event"
          }
        },
        {
          "_icon": "panel",
          "text": "compPagePanel",
          "cls": "Wb.Panel",
          "_expanded": true,
          "properties": {
            "cid": "compPagePanel",
            "layout": "grid1"
          },
          "events": {
            "ready": "let panel = app.compPagePanel;\n\nWb.ajax({\n  url: 'demo-source?xaction=staffFirstRow',\n  json: true,\n  success(data) {\n    panel.add({ cname: 'displayField', cid: 'code', text: 'Code', value: data.code });\n    panel.add({ cname: 'text', cid: 'full_name', text: 'Name', value: data.full_name });\n    panel.add({ cname: 'text', cid: 'birth_date', text: 'Birth Date', value: data.birth_date });\n    panel.add({ cname: 'text', cid: 'email', text: 'Email', value: data.email });\n    panel.add({ cname: 'component', html: 'WebBuilder components' });\n    // Wb.setValue(app.compPagePanel, data); // set compPagePanel data\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: custom-style.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/css/demo.css\"\n]"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "column",
        "padding": "2em",
        "gap": "2em",
        "cls": "d-tab1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "tab",
          "text": "tab1",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "tab1",
            "height": "10em",
            "cls": "d-tab1",
            "activeIndex": "2"
          },
          "items": [
            {
              "_icon": "card",
              "text": "card1",
              "cls": "Wb.Card",
              "properties": {
                "cid": "card1",
                "title": "Card 1"
              }
            },
            {
              "_icon": "card",
              "text": "card2",
              "cls": "Wb.Card",
              "properties": {
                "cid": "card2",
                "title": "Card 2"
              }
            },
            {
              "_icon": "card",
              "text": "card3",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "card3",
                "title": "Card 3"
              }
            },
            {
              "_icon": "card",
              "text": "card4",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "card4",
                "title": "Card 4"
              }
            },
            {
              "_icon": "card",
              "text": "card5",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "card5",
                "title": "Card 5"
              }
            }
          ]
        },
        {
          "_icon": "desktop",
          "text": "viewer1",
          "cls": "Wb.Viewer",
          "properties": {
            "cid": "viewer1",
            "html": "This example demonstrates how to use CSS to customize the style of a component.\n<ol>\n  <li>Set \"links\" property of the \"module\" to [\"wb/css/demo.css\"].</li>\n  <li>Set \"cls\" property of the \"tab1\" to \"w-tab1\".</li>\n  <li>Define the CSS style for \"w-tab1\" in the file \"wb/css/demo.css\".</li>\n</ol>",
            "flex": "1"
          }
        }
      ]
    }
  ]
}

File name: dashboard.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{newWin: true}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "theme": "false",
    "description": "demo-source=example/common/demo-source.xwl"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "grid": "5fr 4fr / repeat(4, 1fr)",
        "defaults": "({ background: false })",
        "background": "true",
        "gap": "1em",
        "padding": "1em"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "layout": "center",
            "background": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "label",
              "text": "titleLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "titleLabel",
                "titleType": "title3",
                "text": "Dashboard Title"
              }
            }
          ]
        },
        {
          "_icon": "chart-bar",
          "text": "stackedAreaChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "stackedAreaChart",
            "option": "({\n  color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],\n  tooltip: {\n    trigger: 'axis',\n    axisPointer: {\n      type: 'cross',\n      label: {\n        backgroundColor: '#6a7985'\n      }\n    }\n  },\n  grid: {\n    left: '3%',\n    right: '4%',\n    bottom: '3%',\n    containLabel: true\n  },\n  xAxis: [\n    {\n      type: 'category',\n      boundaryGap: false,\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    }\n  ],\n  yAxis: [\n    {\n      type: 'value'\n    }\n  ],\n  series: [\n    {\n      name: 'Line 1',\n      type: 'line',\n      stack: 'Total',\n      smooth: true,\n      lineStyle: {\n        width: 0\n      },\n      showSymbol: false,\n      areaStyle: {\n        opacity: 0.8,\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n          {\n            offset: 0,\n            color: 'rgb(128, 255, 165)'\n          },\n          {\n            offset: 1,\n            color: 'rgb(1, 191, 236)'\n          }\n        ])\n      },\n      emphasis: {\n        focus: 'series'\n      },\n      data: [140, 232, 101, 264, 90, 340, 250]\n    },\n    {\n      name: 'Line 2',\n      type: 'line',\n      stack: 'Total',\n      smooth: true,\n      lineStyle: {\n        width: 0\n      },\n      showSymbol: false,\n      areaStyle: {\n        opacity: 0.8,\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n          {\n            offset: 0,\n            color: 'rgb(0, 221, 255)'\n          },\n          {\n            offset: 1,\n            color: 'rgb(77, 119, 255)'\n          }\n        ])\n      },\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 282, 111, 234, 220, 340, 310]\n    },\n    {\n      name: 'Line 3',\n      type: 'line',\n      stack: 'Total',\n      smooth: true,\n      lineStyle: {\n        width: 0\n      },\n      showSymbol: false,\n      areaStyle: {\n        opacity: 0.8,\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n          {\n            offset: 0,\n            color: 'rgb(55, 162, 255)'\n          },\n          {\n            offset: 1,\n            color: 'rgb(116, 21, 219)'\n          }\n        ])\n      },\n      emphasis: {\n        focus: 'series'\n      },\n      data: [320, 132, 201, 334, 190, 130, 220]\n    },\n    {\n      name: 'Line 4',\n      type: 'line',\n      stack: 'Total',\n      smooth: true,\n      lineStyle: {\n        width: 0\n      },\n      showSymbol: false,\n      areaStyle: {\n        opacity: 0.8,\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n          {\n            offset: 0,\n            color: 'rgb(255, 0, 135)'\n          },\n          {\n            offset: 1,\n            color: 'rgb(135, 0, 157)'\n          }\n        ])\n      },\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 402, 231, 134, 190, 230, 120]\n    },\n    {\n      name: 'Line 5',\n      type: 'line',\n      stack: 'Total',\n      smooth: true,\n      lineStyle: {\n        width: 0\n      },\n      showSymbol: false,\n      label: {\n        show: true,\n        position: 'top'\n      },\n      areaStyle: {\n        opacity: 0.8,\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n          {\n            offset: 0,\n            color: 'rgb(255, 191, 0)'\n          },\n          {\n            offset: 1,\n            color: 'rgb(224, 62, 76)'\n          }\n        ])\n      },\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 302, 181, 234, 210, 290, 150]\n    }\n  ]\n})"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "column"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "chart-bar",
              "text": "lineChart",
              "cls": "Wb.Chart",
              "properties": {
                "cid": "lineChart",
                "option": "(f => {\n  const series = [\n    {\n      name: 'Email',\n      type: 'line',\n      stack: 'Total',\n      data: [120, 132, 101, 134, 90, 230, 210]\n    },\n    {\n      name: 'Union Ads',\n      type: 'line',\n      stack: 'Total',\n      data: [220, 182, 191, 234, 290, 330, 310]\n    }\n  ];\n  return {\n    tooltip: {\n      trigger: 'axis'\n    },\n    grid: {\n      left: '3%',\n      right: '4%',\n      bottom: '3%',\n      containLabel: true\n    },\n    xAxis: {\n      type: 'category',\n      boundaryGap: false,\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    },\n    yAxis: {\n      type: 'value'\n    },\n    series\n  };\n})()",
                "flex": "1"
              },
              "_expanded": true
            },
            {
              "_icon": "chart-bar",
              "text": "barChart",
              "cls": "Wb.Chart",
              "properties": {
                "cid": "barChart",
                "option": "({\n  xAxis: {\n    type: 'category',\n    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n  },\n  yAxis: {\n    type: 'value'\n  },\n  series: [\n    {\n      data: [120, 200, 150, 80, 70, 110, 130],\n      type: 'bar',\n      showBackground: true,\n      backgroundStyle: {\n        color: 'rgba(180, 180, 180, 0.2)'\n      }\n    }\n  ]\n})",
                "flex": "1"
              },
              "_expanded": true
            }
          ]
        },
        {
          "_icon": "chart-bar",
          "text": "ringChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "ringChart",
            "option": "(f => {\n  const gaugeData = [\n    {\n      value: 20,\n      name: 'Perfect',\n      title: {\n        offsetCenter: ['0%', '-40%']\n      },\n      detail: {\n        valueAnimation: true,\n        offsetCenter: ['0%', '-15%']\n      }\n    },\n    {\n      value: 40,\n      name: 'Good',\n      title: {\n        offsetCenter: ['0%', '10%']\n      },\n      detail: {\n        valueAnimation: true,\n        offsetCenter: ['0%', '35%']\n      }\n    }\n  ];\n  app.gaugeTimer = setInterval(function () {\n    gaugeData[0].value = +(Math.random() * 100).toFixed(2);\n    gaugeData[1].value = +(Math.random() * 100).toFixed(2);\n    app.ringChart.setOption({\n      series: [\n        {\n          data: gaugeData,\n          pointer: {\n            show: false\n          }\n        }\n      ]\n    });\n  }, 2000);\n  return {\n    series: [\n      {\n        type: 'gauge',\n        startAngle: 90,\n        endAngle: -270,\n        pointer: {\n          show: false\n        },\n        progress: {\n          show: true,\n          overlap: false,\n          roundCap: true,\n          clip: false,\n          itemStyle: {\n            borderWidth: 1,\n            borderColor: '#464646'\n          }\n        },\n        axisLine: {\n          lineStyle: {\n            width: 40\n          }\n        },\n        splitLine: {\n          show: false,\n          distance: 0,\n          length: 10\n        },\n        axisTick: {\n          show: false\n        },\n        axisLabel: {\n          show: false,\n          distance: 50\n        },\n        data: gaugeData,\n        title: {\n          fontSize: 14\n        },\n        detail: {\n          width: 50,\n          height: 14,\n          fontSize: 14,\n          color: 'inherit',\n          borderColor: 'inherit',\n          borderRadius: 20,\n          borderWidth: 1,\n          formatter: '{value}%'\n        }\n      }\n    ]\n  }\n})()"
          },
          "_expanded": true,
          "events": {
            "destroy": "clearInterval(app.gaugeTimer);"
          }
        },
        {
          "_icon": "chart-bar",
          "text": "gaugeChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "gaugeChart",
            "option": "({\n  tooltip: {\n    formatter: '{a} <br/>{b} : {c}%'\n  },\n  series: [\n    {\n      name: 'Pressure',\n      type: 'gauge',\n      detail: {\n        formatter: '{value}'\n      },\n      data: [\n        {\n          value: 50,\n          name: 'SCORE'\n        }\n      ]\n    }\n  ]\n})",
            "flex": "1"
          },
          "_expanded": true
        },
        {
          "_icon": "chart-bar",
          "text": "sunburstChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "sunburstChart",
            "option": "(f => {\n  let data = [\n    {\n      name: 'Grandpa',\n      children: [\n        {\n          name: 'Uncle Leo',\n          value: 15,\n          children: [\n            {\n              name: 'Cousin Jack',\n              value: 2\n            },\n            {\n              name: 'Cousin Mary',\n              value: 5,\n              children: [\n                {\n                  name: 'Jackson',\n                  value: 2\n                }\n              ]\n            },\n            {\n              name: 'Cousin Ben',\n              value: 4\n            }\n          ]\n        },\n        {\n          name: 'Father',\n          value: 10,\n          children: [\n            {\n              name: 'Me',\n              value: 5\n            },\n            {\n              name: 'Brother Peter',\n              value: 1\n            }\n          ]\n        }\n      ]\n    },\n    {\n      name: 'Nancy',\n      children: [\n        {\n          name: 'Uncle Nike',\n          children: [\n            {\n              name: 'Cousin Betty',\n              value: 1\n            },\n            {\n              name: 'Cousin Jenny',\n              value: 2\n            }\n          ]\n        }\n      ]\n    }\n  ];\n  return {\n    series: {\n      type: 'sunburst',\n      data: data,\n      radius: [0, '90%'],\n      label: {\n        rotate: 'radial'\n      }\n    }\n  }\n})()"
          },
          "_expanded": true
        },
        {
          "_icon": "grid",
          "text": "staffGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "staffGrid",
            "flex": "1",
            "url": "demo-source?xaction=staffAllDict",
            "gridColumn": "span 2",
            "title": "Staff List",
            "pagingBar": "false"
          },
          "_expanded": false,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "nameCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "fieldName": "full_name",
                    "text": "Name",
                    "width": "-1"
                  }
                },
                {
                  "_icon": "column",
                  "text": "emailCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "emailCol",
                    "fieldName": "email",
                    "text": "Email",
                    "width": "-1"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "chart-bar",
          "text": "pieChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "pieChart",
            "option": "({\n  tooltip: {\n    trigger: 'item'\n  },\n  series: [\n    {\n      name: 'Access From',\n      type: 'pie',\n      radius: '50%',\n      data: [\n        { value: 1048, name: 'Search Engine' },\n        { value: 735, name: 'Direct' },\n        { value: 580, name: 'Email' },\n        { value: 484, name: 'Union Ads' },\n        { value: 300, name: 'Video Ads' }\n      ],\n      emphasis: {\n        itemStyle: {\n          shadowBlur: 10,\n          shadowOffsetX: 0,\n          shadowColor: 'rgba(0, 0, 0, 0.5)'\n        }\n      }\n    }\n  ]\n})",
            "flex": "1"
          },
          "_expanded": true
        }
      ]
    }
  ]
}

File name: drag-drop.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Drag and Drop",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "sortableTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "sortableTitle",
            "title": "Items sortable"
          }
        },
        {
          "_icon": "container",
          "text": "sortableCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "sortableCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "sortableLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "sortableLabel",
                "text": "Please drag a button to adjust it's position"
              }
            },
            {
              "_icon": "container",
              "text": "itemsSortableCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "itemsSortableCt",
                "layout": "form",
                "itemsSortable": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "button1",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button1",
                    "text": "Button1"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button2",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button2",
                    "text": "Button2"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button3",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button3",
                    "text": "Button3"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button4",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button4",
                    "text": "Button4"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button5",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button5",
                    "text": "Button5"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button6",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button6",
                    "text": "Button6"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button7",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button7",
                    "text": "Button7"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button8",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button8",
                    "text": "Button8"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button9",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button9",
                    "text": "Button9"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "gridRowDragDropTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "gridRowDragDropTitle",
            "title": "Grid row drag and drop"
          }
        },
        {
          "_icon": "grid",
          "text": "commonRemoteGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "commonRemoteGrid",
            "height": "15em",
            "url": "demo-source?xaction=staffAllDict",
            "droppable": "true",
            "draggable": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "dualDragDropTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dualDragDropTitle",
            "title": "Dual drag and drop"
          }
        },
        {
          "_icon": "container",
          "text": "dualDragDropCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "dualDragDropCt",
            "layout": "row",
            "height": "15em",
            "gap": "true"
          },
          "items": [
            {
              "_icon": "grid",
              "text": "grid1",
              "cls": "Wb.Grid",
              "properties": {
                "cid": "grid1",
                "url": "demo-source?xaction=staffAllDict",
                "draggable": "true",
                "title": "Source Grid",
                "flex": "1",
                "droppable": "dualGroup",
                "multiSelect": "true"
              },
              "_expanded": true,
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "columns"
                  },
                  "text": "columns",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column1",
                        "rowNum": "true"
                      },
                      "text": "column1",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column2",
                        "text": "code",
                        "fieldName": "code",
                        "width": "6em"
                      },
                      "text": "column2",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column3",
                        "fieldName": "full_name",
                        "text": "Full name",
                        "width": "-1"
                      },
                      "text": "column3",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "grid",
              "text": "grid2",
              "cls": "Wb.Grid",
              "properties": {
                "cid": "grid2",
                "droppable": "dualGroup",
                "title": "Drag source to here",
                "flex": "1",
                "draggable": "true",
                "multiSelect": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "columns"
                  },
                  "text": "columns",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column1",
                        "rowNum": "true"
                      },
                      "text": "column1",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column2",
                        "text": "code",
                        "fieldName": "code",
                        "width": "6em"
                      },
                      "text": "column2",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column3",
                        "fieldName": "full_name",
                        "text": "Full name",
                        "width": "-1"
                      },
                      "text": "column3",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "dragGridToPanelTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dragGridToPanelTitle",
            "title": "Drag grid to panel"
          }
        },
        {
          "_icon": "container",
          "text": "dragGridToPanelCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "dragGridToPanelCt",
            "layout": "row",
            "height": "15em",
            "gap": "true"
          },
          "items": [
            {
              "_icon": "grid",
              "text": "grid1",
              "cls": "Wb.Grid",
              "properties": {
                "cid": "grid1",
                "url": "demo-source?xaction=staffAllDict",
                "title": "Source Grid",
                "flex": "1",
                "dropGroup": "panelGroup",
                "multiSelect": "true",
                "draggable": "true"
              },
              "_expanded": true,
              "events": {
                "itemdrag": "// only allow odd code rows\ndraggable.allowDrop = draggable.source.every(item => parseInt(item.data.code) % 2);\ndraggable.mode = 'append'; // show append drag icon\ndraggable.autoDrop = false; // prevent default drop behavior",
                "itemdrop": "let items = [];\ndraggable.source.forEach(item => {\n  items.push({ cname: 'text', text: item.data.full_name });\n})\nitems = app.dropPanel.add(items);\nitems[0].intoView();\nitems.each(item => item.highlight());\nWb.destroy(draggable.source); // remove source rows"
              },
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "columns"
                  },
                  "text": "columns",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column1",
                        "rowNum": "true"
                      },
                      "text": "column1",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column2",
                        "text": "code",
                        "fieldName": "code",
                        "width": "6em"
                      },
                      "text": "column2",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "column3",
                        "fieldName": "full_name",
                        "text": "Full name",
                        "width": "-1"
                      },
                      "text": "column3",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "dropPanel",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "dropPanel",
                "title": "Drag odd code rows to here",
                "flex": "1",
                "droppable": "panelGroup",
                "layout": "grid1"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "asyncDragDropTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "asyncDragDropTitle",
            "title": "Asynchronous drag and drop"
          }
        },
        {
          "_icon": "grid",
          "text": "asyncDragDropGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "asyncDragDropGrid",
            "url": "demo-source?xaction=staffAllDict",
            "draggable": "{ autoDrop: false }",
            "droppable": "asyncGroup",
            "height": "15em"
          },
          "_expanded": true,
          "events": {
            "itemdrop": "// do some server side operations\nWb.ajax({\n  url: 'demo-source?xaction=staffDict',\n  params: { code: draggable.source[0].data.code },\n  success(resp) {\n    if (resp.length)\n      draggable.acceptDrop();\n    else\n      Wb.tipWarn('Only allow dragging odd code row.')\n  }\n});"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "column1",
                    "rowNum": "true"
                  },
                  "text": "column1",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "column2",
                    "text": "code",
                    "fieldName": "code",
                    "width": "6em"
                  },
                  "text": "column2",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "column3",
                    "fieldName": "full_name",
                    "text": "Full name",
                    "width": "-1"
                  },
                  "text": "column3",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: geejing-link.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{frame: true, url: 'https://www.geejing.com'}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "description": "Please select this module and click [Properties] button on the IDE toolbar to view properties of this module:\nsee parameters: {frame: true, url: 'https://www.geejing.com'}"
  },
  "_icon": "module",
  "items": []
}

File name: grid1-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "border": "bottom"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              }
            }
          ]
        },
        {
          "_icon": "label",
          "text": "titleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "titleLabel",
            "text": "Employee onboarding",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "basicInfoTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "basicInfoTitle",
            "title": "Basic information"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "name",
          "cls": "Wb.Text",
          "properties": {
            "cid": "name",
            "text": "Name",
            "required": "true"
          }
        },
        {
          "_icon": "combo",
          "text": "gender",
          "cls": "Wb.Select",
          "_expanded": true,
          "properties": {
            "cid": "gender",
            "text": "Gender",
            "keyName": "gender",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "birthdate",
          "cls": "Wb.Date",
          "properties": {
            "cid": "birthdate",
            "text": "Birthdate",
            "required": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "eduInfoTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "eduInfoTitle",
            "title": "Education"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "degree",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "degree",
            "text": "Degree"
          }
        },
        {
          "_icon": "text",
          "text": "university",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "university",
            "text": "University"
          }
        },
        {
          "_icon": "text",
          "text": "major",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "major",
            "text": "Major"
          }
        },
        {
          "_icon": "title",
          "text": "deptTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "deptTitle",
            "title": "Department"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "department",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "department",
            "text": "Department"
          }
        },
        {
          "_icon": "text",
          "text": "duty",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "duty",
            "text": "Duty"
          }
        },
        {
          "_icon": "text",
          "text": "period",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "period",
            "text": "Probation period"
          }
        },
        {
          "_icon": "calendar",
          "text": "entryDate",
          "cls": "Wb.Date",
          "properties": {
            "cid": "entryDate",
            "text": "Entry Date"
          }
        },
        {
          "_icon": "text",
          "text": "project",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "project",
            "text": "Project"
          }
        },
        {
          "_icon": "number-edit",
          "text": "salary",
          "cls": "Wb.Number",
          "properties": {
            "cid": "salary",
            "text": "Salary",
            "minValue": "1000",
            "prefix": "$"
          }
        },
        {
          "_icon": "text",
          "text": "address",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "address",
            "text": "Address"
          }
        },
        {
          "_icon": "title",
          "text": "commentTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commentTitle",
            "title": "Comments"
          },
          "_expanded": true
        },
        {
          "_icon": "textarea",
          "text": "office",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "office",
            "text": "Office",
            "height": "5em"
          }
        },
        {
          "_icon": "textarea",
          "text": "manager",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "manager",
            "text": "Manager",
            "height": "5em"
          }
        },
        {
          "_icon": "textarea",
          "text": "ceo",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "ceo",
            "text": "CEO",
            "height": "5em"
          }
        },
        {
          "_icon": "button",
          "text": "saveBtn",
          "cls": "Wb.Button",
          "_expanded": true,
          "properties": {
            "cid": "saveBtn",
            "icon": "save",
            "text": "Save Form",
            "type": "primary"
          },
          "events": {
            "click": "if (Wb.verify(app.viewport1)) {\n  Wb.ajax({\n    url: xpath,\n    comps: app.viewport1,\n    success() {\n      Wb.tipDone();\n    }\n  });\n}"
          }
        }
      ]
    }
  ]
}

File name: grid3-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid3"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "titleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "titleLabel",
            "text": "Employee onboarding",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "basicInfoTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "basicInfoTitle",
            "title": "Basic information"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "name",
          "cls": "Wb.Text",
          "properties": {
            "cid": "name",
            "text": "Name",
            "required": "true"
          }
        },
        {
          "_icon": "combo",
          "text": "gender",
          "cls": "Wb.Select",
          "_expanded": true,
          "properties": {
            "cid": "gender",
            "text": "Gender",
            "keyName": "gender",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "birthdate",
          "cls": "Wb.Date",
          "properties": {
            "cid": "birthdate",
            "text": "Birthdate",
            "required": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "eduInfoTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "eduInfoTitle",
            "title": "Education"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "degree",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "degree",
            "text": "Degree"
          }
        },
        {
          "_icon": "text",
          "text": "university",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "university",
            "text": "University"
          }
        },
        {
          "_icon": "text",
          "text": "major",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "major",
            "text": "Major"
          }
        },
        {
          "_icon": "title",
          "text": "deptTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "deptTitle",
            "title": "Department"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "department",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "department",
            "text": "Department",
            "gridColumn": "span 2"
          }
        },
        {
          "_icon": "text",
          "text": "duty",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "duty",
            "text": "Duty"
          }
        },
        {
          "_icon": "text",
          "text": "period",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "period",
            "text": "Probation period",
            "gridColumn": "span 2"
          }
        },
        {
          "_icon": "calendar",
          "text": "entryDate",
          "cls": "Wb.Date",
          "properties": {
            "cid": "entryDate",
            "text": "Entry Date"
          }
        },
        {
          "_icon": "text",
          "text": "project",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "project",
            "text": "Project",
            "gridColumn": "span 2"
          }
        },
        {
          "_icon": "number-edit",
          "text": "salary",
          "cls": "Wb.Number",
          "properties": {
            "cid": "salary",
            "text": "Salary",
            "minValue": "1000",
            "prefix": "$"
          }
        },
        {
          "_icon": "text",
          "text": "address",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "address",
            "text": "Address",
            "gridColumn": "span 3"
          }
        },
        {
          "_icon": "title",
          "text": "commentTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commentTitle",
            "title": "Comments"
          },
          "_expanded": true
        },
        {
          "_icon": "textarea",
          "text": "office",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "office",
            "text": "Office",
            "gridColumn": "span 3",
            "height": "5em"
          }
        },
        {
          "_icon": "textarea",
          "text": "manager",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "manager",
            "text": "Manager",
            "gridColumn": "span 3",
            "height": "5em"
          }
        },
        {
          "_icon": "textarea",
          "text": "ceo",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "ceo",
            "text": "CEO",
            "gridColumn": "span 3",
            "height": "5em"
          }
        },
        {
          "_icon": "button",
          "text": "button1",
          "cls": "Wb.Button",
          "_expanded": true,
          "properties": {
            "cid": "button1",
            "icon": "save",
            "text": "Save Form",
            "type": "primary",
            "gridColumn": "span 3"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath,\n  comps: app.viewport1,\n  success() {\n    Wb.tipDone();\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: layout.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Layout",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "label",
          "text": "descLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "descLabel",
            "text": "Set the layout property to specify the layout of child components."
          }
        },
        {
          "_icon": "title",
          "text": "rowTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "rowTitle",
            "title": "Row"
          }
        },
        {
          "_icon": "label",
          "text": "commonRowLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "commonRowLabel",
            "text": "Common Row"
          }
        },
        {
          "_icon": "container",
          "text": "rowCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "unsetBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "unsetBtn",
                "text": "Unset",
                "minWidth": "5em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "setWidthBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "setWidthBtn",
                "text": "Set Width",
                "width": "10em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "flex1Btn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "flex1Btn",
                "text": "Flex1",
                "flex": "1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "flex2Btn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "flex2Btn",
                "text": "Flex2",
                "flex": "2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "customFlexBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "customFlexBtn",
                "flex": "2 0 12em",
                "text": "Custom Flex"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "autoWrapRowLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "autoWrapRowLabel",
            "text": "Row with auto wrap"
          }
        },
        {
          "_icon": "container",
          "text": "autoWrapRowCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "autoWrapRowCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "flexWrap": "true",
            "gap": ".5em",
            "padding": "true",
            "resizable": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2",
                "width": "20em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "width": "30em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button4",
                "text": "Button4",
                "width": "40em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button5",
                "text": "Button5"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithFlexLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithFlexLabel",
            "text": "Row with flex"
          }
        },
        {
          "_icon": "container",
          "text": "rowFlexCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowFlexCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "padding": "true",
            "gap": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "unsetBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "unsetBtn",
                "text": "Unset"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "setWidthBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "setWidthBtn",
                "text": "Set Width",
                "width": "10em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "flex1Btn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "flex1Btn",
                "text": "Flex1",
                "flex": "1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "flex2Btn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "flex2Btn",
                "text": "Flex2",
                "flex": "2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "customFlexBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "customFlexBtn",
                "flex": "2 0 12em",
                "text": "Custom Flex"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithJustifyCenterLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithJustifyCenterLabel",
            "text": "Row with justify center"
          }
        },
        {
          "_icon": "container",
          "text": "rowWithJustifyCenterCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowWithJustifyCenterCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "justify": "center"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button3",
                "text": "Button3"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithAlignJustifyLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithAlignJustifyLabel",
            "text": "Row with align and justify"
          }
        },
        {
          "_icon": "container",
          "text": "rowWithAlignJustifyCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowWithAlignJustifyCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "align": "center",
            "justify": "end",
            "height": "8em",
            "gap": ".5em",
            "padding": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Button1",
                "alignSelf": "start"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "alignSelf": "end"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button4",
                "text": "Button4",
                "alignSelf": "stretch"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithBetweenLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithBetweenLabel",
            "text": "Row with justify between"
          }
        },
        {
          "_icon": "container",
          "text": "rowWithBetweenCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowWithBetweenCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "align": "center",
            "justify": "space-between",
            "height": "8em",
            "gap": ".5em",
            "padding": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Button1",
                "alignSelf": "start"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "alignSelf": "end"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button4",
                "text": "Button4",
                "alignSelf": "stretch"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithAroundLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithAroundLabel",
            "text": "Row with justify around"
          }
        },
        {
          "_icon": "container",
          "text": "rowWithAroundCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowWithAroundCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true",
            "align": "center",
            "justify": "space-around",
            "height": "8em",
            "gap": ".5em",
            "padding": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Button1",
                "alignSelf": "start"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "alignSelf": "end"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button4",
                "text": "Button4",
                "alignSelf": "stretch"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "rowWithFillLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "rowWithFillLabel",
            "text": "Row with fill"
          }
        },
        {
          "_icon": "container",
          "text": "rowWithFillCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowWithFillCt",
            "layout": "row",
            "frame": "true",
            "autoScroll": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "_expanded": false,
              "properties": {
                "cid": "fill1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button3",
                "text": "Button3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "right1",
              "text": "fill2",
              "cls": "Wb.Fill",
              "_expanded": true,
              "properties": {
                "cid": "fill2"
              }
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "columnTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "columnTitle",
            "title": "Column"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "columnCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "columnCt",
            "layout": "column",
            "frame": "true",
            "autoScroll": "true",
            "height": "15em",
            "width": "15em"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "unsetBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "unsetBtn",
                "text": "Unset",
                "minWidth": "5em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "setHeightBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "setHeightBtn",
                "text": "Set Height",
                "height": "3em"
              }
            },
            {
              "_icon": "button",
              "text": "flex1Btn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "flex1Btn",
                "text": "Flex1",
                "flex": "1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "customFlexBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "customFlexBtn",
                "flex": "2 0 1em",
                "text": "Custom Flex"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "plsReferRowLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "plsReferRowLabel",
            "text": "Please refer to the above Row demos for more Column usages"
          }
        },
        {
          "_icon": "title",
          "text": "rowColBordersTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "rowColBordersTitle",
            "title": "Row Column borders"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "rowColBordersCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "rowColBordersCt",
            "layout": "column",
            "frame": "true",
            "height": "20em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "topCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "topCt",
                "text": "Top",
                "layout": "center",
                "height": "5em",
                "frame": "true"
              }
            },
            {
              "_icon": "splitter",
              "text": "splitter1",
              "cls": "Wb.Splitter",
              "properties": {
                "cid": "splitter1"
              }
            },
            {
              "_icon": "container",
              "text": "middleCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "middleCt",
                "layout": "row",
                "flex": "1",
                "frame": "true"
              },
              "_expanded": true,
              "hasDupCid": 1,
              "items": [
                {
                  "_icon": "container",
                  "text": "leftCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "leftCt",
                    "text": "Floatable Left",
                    "width": "10em",
                    "layout": "center",
                    "frame": "true"
                  }
                },
                {
                  "_icon": "splitter",
                  "text": "splitter2",
                  "cls": "Wb.Splitter",
                  "properties": {
                    "cid": "splitter2",
                    "enterShowTarget": "true"
                  }
                },
                {
                  "_icon": "container",
                  "text": "centerCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "centerCt",
                    "text": "Center",
                    "flex": "1",
                    "layout": "center",
                    "frame": "true"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "splitter",
                  "text": "splitter3",
                  "cls": "Wb.Splitter",
                  "properties": {
                    "cid": "splitter3"
                  }
                },
                {
                  "_icon": "container",
                  "text": "rightCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "rightCt",
                    "text": "Right",
                    "width": "10em",
                    "layout": "center",
                    "frame": "true"
                  }
                }
              ]
            },
            {
              "_icon": "splitter",
              "text": "splitter4",
              "cls": "Wb.Splitter",
              "properties": {
                "cid": "splitter4"
              }
            },
            {
              "_icon": "container",
              "text": "bottomCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "bottomCt",
                "text": "Bottom",
                "height": "5em",
                "layout": "center",
                "frame": "true"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "formTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "formTitle",
            "title": "Form"
          }
        },
        {
          "_icon": "label",
          "text": "commonFormLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "commonFormLabel",
            "text": "Common Form"
          }
        },
        {
          "_icon": "container",
          "text": "formCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "formCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2",
                "width": "20em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "width": "30em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button5",
                "text": "Button5"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button6",
                "text": "Button6"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button7",
                "text": "Button7",
                "width": "25em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button8",
                "text": "Button8"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button9",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button9",
                "text": "Button9"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "formWithoutGapLabel",
          "cls": "Wb.Label",
          "_expanded": false,
          "properties": {
            "cid": "formWithoutGapLabel",
            "text": "Form without gap"
          }
        },
        {
          "_icon": "container",
          "text": "formWithoutGapCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "formWithoutGapCt",
            "layout": "form",
            "frame": "true",
            "padding": "true",
            "gap": "false"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2",
                "width": "20em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Button3",
                "width": "30em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button5",
                "text": "Button5"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button6",
                "text": "Button6"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button7",
                "text": "Button7",
                "width": "25em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button8",
                "text": "Button8"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button9",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button9",
                "text": "Button9"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "form1Label",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "form1Label",
            "text": "Form1 with textArea vertical flex"
          }
        },
        {
          "_icon": "container",
          "text": "form1Ct",
          "cls": "Wb.Container",
          "properties": {
            "cid": "form1Ct",
            "layout": "form1",
            "frame": "true",
            "height": "15em",
            "resizable": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "text1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "textarea",
              "text": "textArea1",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "textArea1",
                "text": "textArea",
                "flex": "1",
                "minHeight": "3em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text2",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text2",
                "text": "text2"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "gridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "gridTitle",
            "title": "Grid"
          }
        },
        {
          "_icon": "container",
          "text": "gridCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "gridCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Button3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button5",
                "text": "Button5"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button6",
                "text": "Button6"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button7",
                "text": "Button7"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button8",
                "text": "Button8"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button9",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button9",
                "text": "Button9"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "customGridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "customGridTitle",
            "title": "Grid with custom gap and padding"
          }
        },
        {
          "_icon": "container",
          "text": "customGridCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "customGridCt",
            "layout": "grid",
            "frame": "true",
            "gap": ".5em",
            "padding": "3em"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Button3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button5",
                "text": "Button5"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button6",
                "text": "Button6"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button7",
                "text": "Button7"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button8",
                "text": "Button8"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button9",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button9",
                "text": "Button9"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "grid1Title",
          "cls": "Wb.Title",
          "properties": {
            "cid": "grid1Title",
            "title": "Grid with 1 column"
          }
        },
        {
          "_icon": "container",
          "text": "grid1Ct",
          "cls": "Wb.Container",
          "properties": {
            "cid": "grid1Ct",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Button3"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "grid5Title",
          "cls": "Wb.Title",
          "properties": {
            "cid": "grid5Title",
            "title": "Grid with 5 columns"
          }
        },
        {
          "_icon": "container",
          "text": "grid5Ct",
          "cls": "Wb.Container",
          "properties": {
            "cid": "grid5Ct",
            "layout": "grid5",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Span 3",
                "gridColumn": "span 3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Button4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button5",
                "text": "Span 3 / 2",
                "gridColumn": "span 3",
                "gridRow": "span 2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button6",
                "text": "Button6"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button7",
                "text": "Button7"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button8",
                "text": "Button8"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button9",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button9",
                "text": "Button9"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "component",
              "text": "component1",
              "cls": "Wb.Component",
              "properties": {
                "cid": "component1"
              }
            },
            {
              "_icon": "button",
              "text": "button10",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button10",
                "text": "Button10"
              }
            },
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "fill1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button11",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button11",
                "text": "Button11"
              }
            },
            {
              "_icon": "button",
              "text": "button12",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button12",
                "text": "Button12"
              }
            },
            {
              "_icon": "space",
              "text": "space1",
              "cls": "Wb.Space",
              "properties": {
                "cid": "space1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button13",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button13",
                "text": "Button13"
              }
            },
            {
              "_icon": "container",
              "text": "container1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container1",
                "layout": "row",
                "gap": ".5em",
                "defaults": "({ flex: 1 })",
                "gridColumn": "span 2"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "button14",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button14",
                    "text": "Sub1"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button15",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button15",
                    "text": "Sub2"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button16",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button16",
                    "text": "Sub3"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "gridTableStyleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "gridTableStyleTitle",
            "title": "Grid Table Style"
          }
        },
        {
          "_icon": "container",
          "text": "gridTableStyleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "gridTableStyleCt",
            "layout": "grid3",
            "frame": "true",
            "tableStyle": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "text1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text2",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text2",
                "text": "text2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text3",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text3",
                "text": "text3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text4",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text4",
                "text": "text4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "fill1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text5",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text5",
                "text": "text5"
              }
            },
            {
              "_icon": "text",
              "text": "text6",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text6",
                "text": "text6",
                "gridColumn": "span 2"
              }
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line1",
                "title": "Split Line",
                "dashed": "true",
                "dimTitle": "true"
              }
            },
            {
              "_icon": "text",
              "text": "text7",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text7",
                "text": "text7"
              }
            },
            {
              "_icon": "text",
              "text": "text8",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text8",
                "text": "text8"
              }
            },
            {
              "_icon": "space",
              "text": "space1",
              "cls": "Wb.Space",
              "_expanded": true,
              "properties": {
                "cid": "space1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "toggleTableStyleBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "toggleTableStyleBtn",
                "text": "Toggle Table Style"
              },
              "events": {
                "click": "app.gridTableStyleCt.tableStyle = !app.gridTableStyleCt.tableStyle;"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "gridLastRowAutoFit",
          "cls": "Wb.Title",
          "properties": {
            "cid": "gridLastRowAutoFit",
            "title": "Grid Last Row Auto Fit"
          }
        },
        {
          "_icon": "container",
          "text": "gridLastRowAutoFitCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "gridLastRowAutoFitCt",
            "layout": "grid2",
            "frame": "true",
            "gridTplRows": "auto auto 1fr",
            "height": "15em"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "text1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text2",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text2",
                "text": "text2"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text3",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text3",
                "text": "text3"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text4",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "text4",
                "text": "text4"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "textarea",
              "text": "textArea1",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "textArea1",
                "gridColumn": "span 2"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "fitTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "fitTitle",
            "title": "Fit"
          }
        },
        {
          "_icon": "container",
          "text": "fitCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "fitCt",
            "layout": "fit",
            "frame": "true",
            "height": "5em",
            "width": "15em",
            "padding": "1em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Auto fit to container"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "centerTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "centerTitle",
            "title": "Center"
          }
        },
        {
          "_icon": "label",
          "text": "centerDescLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "centerDescLabel",
            "text": "The center point of the child control is always located at the center point of the container, crop components when it overflow."
          }
        },
        {
          "_icon": "container",
          "text": "centerCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "centerCt",
            "layout": "center",
            "frame": "true",
            "height": "5em",
            "resizable": "true",
            "autoScroll": "true"
          },
          "_expanded": false,
          "hasDupCid": 1,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Center",
                "width": "20em",
                "height": "3em"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "middleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "middleTitle",
            "title": "Middle"
          }
        },
        {
          "_icon": "label",
          "text": "middleDesc",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "middleDesc",
            "text": "Center horizontally and vertically. Do not crop components when it overflow."
          }
        },
        {
          "_icon": "container",
          "text": "middleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "middleCt",
            "layout": "middle",
            "frame": "true",
            "height": "5em",
            "autoScroll": "true",
            "resizable": "true"
          },
          "_expanded": false,
          "hasDupCid": 1,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "Middle",
                "width": "20em",
                "height": "3em"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "hmiddleDesc",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "hmiddleDesc",
            "text": "Center horizontally. Do not crop components when it overflow."
          }
        },
        {
          "_icon": "container",
          "text": "hmiddleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "hmiddleCt",
            "layout": "hmiddle",
            "frame": "true",
            "height": "5em",
            "autoScroll": "true",
            "resizable": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "hMiddle",
                "width": "20em",
                "height": "3em"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "label",
          "text": "vmiddleDesc",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "vmiddleDesc",
            "text": "Center vertically. Do not crop components when it overflow."
          }
        },
        {
          "_icon": "container",
          "text": "vmiddleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "vmiddleCt",
            "layout": "vmiddle",
            "frame": "true",
            "height": "8em",
            "autoScroll": "true",
            "resizable": "true"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button1",
                "text": "vMiddle",
                "width": "20em",
                "height": "3em"
              },
              "hasDupCid": 1
            }
          ]
        }
      ]
    }
  ]
}

File name: module-bind-ui.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.set({ delDisabled: !Wb.permit('example/crud/dict-edit-dialog/del') })"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "html": "<div>This example demonstrates how a component's \"visible\" property bind to a specified module. If the module\n  is accessible, it is visible, otherwise it is hidden.</div>\n<ul>\n  <li>addBtn is binded to example/crud/dict-edit-dialog/add.xwl</li>\n  <li>editBtn is binded to example/crud/dict-edit-dialog/edit.xwl</li>\n  <li>delBtn \"disabled\" property is binded to \"delDisabled\" which set in serverScript.</li>\n</ul>",
        "padding": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E",
                "bindModule": "example/crud/dict-edit-dialog/add.xwl"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J",
                "bindModule": "example/crud/dict-edit-dialog/edit.xwl"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D",
                "disabled": "_$delDisabled$_"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: no-container.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{container: false}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "main",
        "closeAction": "destroy",
        "width": "50em",
        "height": "20em",
        "title": "No container module",
        "visible": "true",
        "html": "<p>\n  This example demonstrates that there is no default container (usually a tab card) when the module is displayed, and\n  the\n  entire module is automatically destroyed when the main window is closed.\n</p>\n<p>\n  The module has parameters: {container: false}.\n  Please click the [Properties] button to view the module parameters on the IDE.\n</p>",
        "padding": "true",
        "autoScroll": "true"
      },
      "events": {
        "destroy": "Wb.tip('The module is destroyed.');"
      }
    }
  ]
}

File name: performance.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "autoScroll": "true",
        "html": "<div class='w-html'>\n  <p>Applications built with WebBuilder are high-performance, and the client-applications and\n    server-applications can be deployed separately as independent applications or merged into a single application.</p>\n  <div>Client applications:</div>\n  <ul>\n    <li>Efficient algorithms and refined code are used in JavaScript.</li>\n    <li>The construction of the page uses effective methods to prevent page redraw and reflow.</li>\n    <li>Only uses the native CSS on the page layout without using JavaScript to calculate the layout.</li>\n    <li>Advanced component library allows users to experience smooth and live effects.</li>\n    <li>Modern JS and CSS are used in the client application.</li>\n  </ul>\n  <div>Server applications:</div>\n  <ul>\n    <li>Efficient algorithms and refined code enable programs to run faster with fewer resources.</li>\n    <li>JavaScript on the sytem leverages the compiler to provided unparalleled performance.</li>\n    <li>The system caches multiple optimized and preloaded execution contexts to support high concurrency.</li>\n    <li>Share data and code with other languages like Java, Ruby, Python, LLVM, R, and others.</li>\n    <li>JavaScript compatible with the latest ECMAScript specification.</li>\n  </ul>\n</div>"
      }
    }
  ]
}

File name: chart.xwl


{
  "title": "",
  "icon": "chart-bar",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "items": [
        {
          "_icon": "chart-bar",
          "text": "chart1",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "chart1",
            "option": "({\n  legend: false,\n  grid: {\n    left: 15,\n    right: 15,\n    bottom: 15,\n    top: 15,\n    containLabel: true\n  },\n  xAxis: [\n    {\n      type: 'category',\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    }\n  ],\n  yAxis: [\n    {\n      type: 'value'\n    }\n  ],\n  series: [\n    {\n      name: 'Direct',\n      type: 'bar',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [320, 332, 301, 334, 390, 330, 320]\n    },\n    {\n      name: 'Email',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 90, 230, 210]\n    },\n    {\n      name: 'Union Ads',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 182, 191, 234, 290, 330, 310]\n    },\n    {\n      name: 'Video Ads',\n      type: 'bar',\n      stack: 'Ad',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [150, 232, 201, 154, 190, 330, 410]\n    },\n    {\n      name: 'Search Engine',\n      type: 'bar',\n      data: [862, 1018, 964, 1026, 1079, 980, 1030],\n      emphasis: {\n        focus: 'series'\n      },\n      markLine: {\n        lineStyle: {\n          type: 'dashed'\n        },\n        data: [[{ type: 'min' }, { type: 'max' }]]\n      }\n    },\n    {\n      name: 'Baidu',\n      type: 'bar',\n      barWidth: 5,\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [620, 732, 701, 734, 1090, 1130, 1120]\n    },\n    {\n      name: 'Google',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 290, 230, 220]\n    },\n    {\n      name: 'Bing',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [60, 72, 71, 74, 190, 130, 110]\n    },\n    {\n      name: 'Others',\n      type: 'bar',\n      stack: 'Search Engine',\n      emphasis: {\n        focus: 'series'\n      },\n      data: [62, 82, 91, 84, 109, 110, 120]\n    }\n  ]\n})",
            "roundBorder": "true",
            "flex": "1"
          }
        }
      ]
    }
  ]
}

File name: hint.xwl


{
  "title": "",
  "icon": "info1",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "// don't show titlebar in home\n// app.noTitlebar = true;"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "html": "<ul>\n  <li>Drag the left tree node to here to create a new module panel.</li>\n  <li>Drag the panel to adjust its position.</li>\n  <li>Set [app.noTitlebar = true] in module to auto hide title bar.</li>\n  <li>Click panel \"menu button\" to set panel row and column span.</li>\n  <li>Click panel \"maximize button\" to maximize panel.</li>\n  <li>Click home menu \"save desktop\" to save these panels.</li>\n</ul>",
        "textSelectable": "false",
        "cls": "w-lh",
        "autoScroll": "true"
      }
    }
  ]
}

File name: mailbox.xwl


{
  "title": "",
  "icon": "mail",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "title": "My Emails",
        "icon": "mail"
      },
      "items": [
        {
          "cls": "Wb.Array",
          "properties": {
            "cid": "tools"
          },
          "text": "tools",
          "_expanded": true,
          "_icon": "array",
          "items": [
            {
              "_icon": "item",
              "text": "item1",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item1",
                "icon": "gear"
              }
            },
            {
              "_icon": "item",
              "text": "item2",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item2",
                "icon": "preview"
              }
            },
            {
              "_icon": "item",
              "text": "item3",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item3",
                "icon": "close"
              },
              "events": {
                "click": "app.mailGrid.close();"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "flex": "1",
            "plainTitle": "true",
            "roundBorder": "true",
            "localData": "[\n  { title: 'Quarterly Sales Report', from: 'sales@geejing.com', date: new Date().addHour(-1), unread: 1 },\n  { title: 'Urgent: Action Required on Contract Amendments', from: 'legal@geejing.com', date: new Date().addHour(-2), unread: 1 },\n  { title: 'Invitation to the Annual Employee Recognition Ceremony', from: 'hr@geejing.com', date: new Date().addHour(-3) },\n  { title: 'Reminder: Product Launch Meeting on Tuesday', from: 'marketing@geejing.com', date: new Date().addHour(-4) },\n  { title: 'Upcoming IT Maintenance', from: 'it@geejing.com', date: new Date().addHour(-5) },\n  { title: 'Customer Feedback Summary', from: 'customer@geejing.com', date: new Date().addHour(-6) },\n  { title: 'New Policy Implementation: Employee Travel Guidelines', from: 'management@geejing.com', date: new Date().addHour(-7) },\n  { title: 'Feedback Request: Recent Website Updates', from: 'dev@geejing.com', date: new Date().addHour(-8) }\n]",
            "columnsSortable": "true",
            "sorters": "{name: 'date', desc: true}"
          },
          "events": {
            "itemclick": "Wb.tip('Open email \"' + item.data.title + '\".');"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "fieldName": "date",
                    "text": "Date",
                    "render": "el.insertCellIcon('mail', value.dateTimeText);\nel.parentNode.setCls(data.unread, 'w-bold');\nel.parentNode.cls = 'w-pointer';",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "fromCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fromCol",
                    "fieldName": "from",
                    "text": "From"
                  }
                },
                {
                  "_icon": "column",
                  "text": "titleCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "titleCol",
                    "fieldName": "title",
                    "text": "Title",
                    "width": "-1"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: todo.xwl


{
  "title": "",
  "icon": "form",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "title": "To Do List",
        "icon": "form"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Array",
          "properties": {
            "cid": "tools"
          },
          "text": "tools",
          "_expanded": true,
          "_icon": "array",
          "items": [
            {
              "_icon": "item",
              "text": "item1",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item1",
                "icon": "gear"
              }
            },
            {
              "_icon": "item",
              "text": "item2",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item2",
                "icon": "share"
              }
            },
            {
              "_icon": "item",
              "text": "item3",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item3",
                "icon": "close"
              },
              "events": {
                "click": "app.todoView.close();"
              }
            }
          ]
        },
        {
          "_icon": "list-view",
          "text": "view1",
          "cls": "Wb.View",
          "properties": {
            "cid": "view1",
            "itemTpl": "<div class=\"w-row w-align-center w-gapd5 w-padding\">\n  <div class=\"w-icon icon-{if data.done}check9{else}check1{/if}\"></div>\n  <div class=\"w-name\">{text}</div>\n</div>",
            "layout": "column",
            "data": "[\n  { text: 'To complete the project report by the end of this week.', done: 1 },\n  { text: 'To schedule a meeting with my team to discuss the progress of the project.', done: 1 },\n  { text: 'To review the financial reports and identify any areas of concern.' },\n  { text: 'To prepare a presentation for the upcoming board meeting.' },\n  { text: 'To catch up on any outstanding emails and replies.' },\n  { text: 'To update the company website with the latest news and announcements.' },\n  { text: 'To research potential new business opportunities and identify potential markets.' },\n  { text: 'To organize a training session for new employees to familiarize them with company policies and procedures.' }\n]",
            "flex": "1",
            "plainTitle": "true",
            "roundBorder": "true"
          },
          "_expanded": true
        }
      ]
    }
  ]
}

File name: render-to-html.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "array",
      "text": "bindComps",
      "cls": "Wb.Array",
      "properties": {
        "cid": "bindComps"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "text",
          "text": "text1",
          "cls": "Wb.Text",
          "properties": {
            "cid": "text1",
            "instanced": "false"
          }
        },
        {
          "_icon": "calendar",
          "text": "date1",
          "cls": "Wb.Date",
          "properties": {
            "cid": "date1",
            "instanced": "false"
          }
        },
        {
          "_icon": "button",
          "text": "makeBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "makeBtn",
            "text": "Make",
            "instanced": "false"
          },
          "events": {
            "click": "app.renderComp.make();"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1",
        "autoScroll": "true"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "title",
          "text": "title1",
          "cls": "Wb.Title",
          "properties": {
            "cid": "title1",
            "title": "Create comps in HTML"
          }
        },
        {
          "_icon": "label",
          "text": "descLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "descLabel",
            "text": "This example demonstrates how to create comps in HTML."
          }
        },
        {
          "_icon": "component",
          "text": "renderComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "renderComp",
            "html": "<table class=\"w-table w-frame\">\n  <tr>\n    <td>text1</td>\n    <td>\n      <div obj='text1'>\n    </td>\n  </tr>\n  <tr>\n    <td>number1</td>\n    <td>\n      <div obj='{cname: \"number\", cid: \"number1\"}'></div>\n    </td>\n  </tr>\n  <tr>\n    <td>date1</td>\n    <td>\n      <div obj='date1'></div>\n    </td>\n  </tr>\n  <tr>\n    <td>Manual make</td>\n    <td>\n      <div obj='makeBtn'></div>\n    </td>\n  </tr>\n  <tr>\n    <td>Set Value</td>\n    <td>\n      <div\n        obj=\"{ cname: 'button', text: 'Set', handler(){Wb.setValue(this.app.renderComp, { text1: 'foo', number1: 123, date1: new Date() }); }}\">\n      </div>\n    </td>\n  </tr>\n  <tr>\n    <td>Get Value</td>\n    <td>\n      <div obj=\"{ cname: 'button', text: 'Get', handler(){Wb.tip(Wb.encode(Wb.getValue(this.app.renderComp))); }}\">\n      </div>\n    </td>\n  </tr>\n</table>",
            "autoMake": "true"
          }
        },
        {
          "_icon": "title",
          "text": "title2",
          "cls": "Wb.Title",
          "properties": {
            "cid": "title2",
            "title": "Create comp to the specified element"
          }
        },
        {
          "_icon": "component",
          "text": "htmlComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "htmlComp",
            "html": "<div class=\"w-row-center w-gap w-margin-bottom\">\n  <div class=\"w-icon icon-info1\"></div>\n  <div>Please click \"Create Date Comp\" button to create a new Date component below.</div>\n</div>\n<div class=\"my-date w-justifys-start\" style=\"width:15em\"></div>"
          }
        },
        {
          "_icon": "button",
          "text": "button1",
          "cls": "Wb.Button",
          "properties": {
            "cid": "button1",
            "text": "Create Date Comp",
            "justifySelf": "start"
          },
          "events": {
            "click": "app.myDate = new Wb.Date({ owner: app.viewport1, text: 'Date', el: app.viewport1.el.query('.my-date') });\nthis.destroy(); // destroy this button "
          }
        }
      ]
    }
  ]
}

File name: scrollbar.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Scrollbar",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "defaultTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "defaultTitle",
            "title": "Overflow visible by default"
          }
        },
        {
          "_icon": "component",
          "text": "defaultComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "defaultComp",
            "html": "No scrollbar by default<br>\nand overflow content will be displayed<br>\n(overflow content)",
            "height": "3em",
            "frame": "true",
            "margin": "0 0 1em 0",
            "padding": "true"
          }
        },
        {
          "_icon": "title",
          "text": "overflowHiddenTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "overflowHiddenTitle",
            "title": "Overflow hidden"
          }
        },
        {
          "_icon": "component",
          "text": "overflowHiddenComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "overflowHiddenComp",
            "html": "No scrollbar<br>\nand overflow content will be hidden<br>\n(overflow content)",
            "height": "3em",
            "frame": "true",
            "padding": "true",
            "autoScroll": "false"
          }
        },
        {
          "_icon": "title",
          "text": "overflowScrollTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "overflowScrollTitle",
            "title": "Overflow scroll"
          }
        },
        {
          "_icon": "component",
          "text": "overflowScrollComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "overflowScrollComp",
            "html": "Enable scrollbar<br>\nscrollbar is auto<br>\n(overflow content)",
            "height": "3em",
            "frame": "true",
            "padding": "true",
            "autoScroll": "true"
          }
        },
        {
          "_icon": "title",
          "text": "overflowScrollAutoTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "overflowScrollAutoTitle",
            "title": "Overflow scroll with auto show scrollbar"
          }
        },
        {
          "_icon": "component",
          "text": "overflowScrollAutoComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "overflowScrollAutoComp",
            "html": "Enable scrollbar<br>\ndisplay scrollbars only when hovering<br>\n(overflow content)",
            "height": "3em",
            "frame": "true",
            "padding": "true",
            "autoScroll": "auto"
          }
        },
        {
          "_icon": "title",
          "text": "overflowScrollNoBarTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "overflowScrollNoBarTitle",
            "title": "Overflow scroll without show scrollbar"
          }
        },
        {
          "_icon": "component",
          "text": "overflowScrollNoBarComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "overflowScrollNoBarComp",
            "html": "Enable scrollbar<br>\nalways hide scroll bars<br>\n(overflow content)",
            "height": "3em",
            "frame": "true",
            "padding": "true",
            "autoScroll": "hide"
          }
        }
      ]
    }
  ]
}

File name: search-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "column",
        "background": "true",
        "gap": ".7em",
        "padding": ".7em"
      },
      "items": [
        {
          "_icon": "label",
          "text": "label1",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "label1",
            "text": "Search Form",
            "titleType": "title3"
          }
        },
        {
          "_icon": "panel",
          "text": "formPanel",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "formPanel",
            "layout": "row",
            "roundBorder": "true",
            "gap": "true",
            "padding": "true",
            "shrink": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Name",
                "placeholder": "search",
                "width": "15em"
              }
            },
            {
              "_icon": "combo",
              "text": "select1",
              "cls": "Wb.Select",
              "properties": {
                "cid": "select1",
                "text": "Type1",
                "width": "15em"
              }
            },
            {
              "_icon": "combo",
              "text": "select2",
              "cls": "Wb.Select",
              "properties": {
                "cid": "select2",
                "text": "Type2",
                "width": "15em"
              }
            },
            {
              "_icon": "combo",
              "text": "select3",
              "cls": "Wb.Select",
              "properties": {
                "cid": "select3",
                "text": "Type3",
                "width": "15em"
              }
            },
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "type": "primary",
                "text": "Search",
                "icon": "search",
                "shrinkSelf": "false"
              }
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button2",
                "text": "Reset",
                "icon": "undo",
                "shrinkSelf": "false"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "demo-source?xaction=staffAllDict",
            "flex": "1"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true",
                "padding": "true",
                "gap": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "button",
                  "text": "addBtn",
                  "cls": "Wb.Button",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "type": "primary"
                  }
                },
                {
                  "_icon": "button",
                  "text": "editBtn",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "editBtn",
                    "text": "@Str.edit",
                    "icon": "edit"
                  }
                },
                {
                  "_icon": "button",
                  "text": "delBtn",
                  "cls": "Wb.Button",
                  "_expanded": false,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: table-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "middle",
        "autoScroll": "true"
      },
      "items": [
        {
          "_icon": "container",
          "text": "gridCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "gridCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "titleLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "titleLabel",
                "text": "Approval Form",
                "titleType": "title2",
                "textAlign": "center"
              }
            },
            {
              "_icon": "container",
              "text": "formCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "formCt",
                "layout": "grid4",
                "tableStyle": "true",
                "gridTplColumns": "auto 1fr 1fr 1fr",
                "padding": "false",
                "width": "70em",
                "defaults": "({ labelSeparator: false })",
                "background": "true",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "box-label",
                  "text": "generalLabel",
                  "cls": "Wb.BoxLabel",
                  "properties": {
                    "cid": "generalLabel",
                    "text": "General",
                    "gridRow": "span 6",
                    "padding": "true"
                  }
                },
                {
                  "_icon": "text",
                  "text": "name",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "name",
                    "text": "Name",
                    "required": "true"
                  }
                },
                {
                  "_icon": "combo",
                  "text": "gender",
                  "cls": "Wb.Select",
                  "_expanded": true,
                  "properties": {
                    "cid": "gender",
                    "text": "Gender",
                    "keyName": "gender",
                    "required": "true"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "birthdate",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "birthdate",
                    "text": "Birthdate",
                    "required": "true"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "degree",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "degree",
                    "text": "Degree"
                  }
                },
                {
                  "_icon": "text",
                  "text": "university",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "university",
                    "text": "University"
                  }
                },
                {
                  "_icon": "text",
                  "text": "major",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "major",
                    "text": "Major"
                  }
                },
                {
                  "_icon": "text",
                  "text": "department",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "department",
                    "text": "Department",
                    "gridColumn": "span 2"
                  }
                },
                {
                  "_icon": "text",
                  "text": "duty",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "duty",
                    "text": "Duty"
                  }
                },
                {
                  "_icon": "text",
                  "text": "period",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "period",
                    "text": "Probation period",
                    "gridColumn": "span 2"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "entryDate",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "entryDate",
                    "text": "Entry Date"
                  }
                },
                {
                  "_icon": "text",
                  "text": "project",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "project",
                    "text": "Project",
                    "gridColumn": "span 2"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "salary",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "salary",
                    "text": "Salary",
                    "minValue": "1000",
                    "prefix": "$"
                  }
                },
                {
                  "_icon": "text",
                  "text": "address",
                  "cls": "Wb.Text",
                  "_expanded": true,
                  "properties": {
                    "cid": "address",
                    "text": "Address",
                    "gridColumn": "span 3"
                  }
                },
                {
                  "_icon": "box-label",
                  "text": "commentsLabel",
                  "cls": "Wb.BoxLabel",
                  "properties": {
                    "cid": "commentsLabel",
                    "text": "Comments",
                    "gridRow": "span 3",
                    "padding": "true"
                  }
                },
                {
                  "_icon": "textarea",
                  "text": "office",
                  "cls": "Wb.TextArea",
                  "properties": {
                    "cid": "office",
                    "text": "Office",
                    "gridColumn": "span 3",
                    "height": "5em"
                  }
                },
                {
                  "_icon": "textarea",
                  "text": "manager",
                  "cls": "Wb.TextArea",
                  "properties": {
                    "cid": "manager",
                    "text": "Manager",
                    "gridColumn": "span 3",
                    "height": "5em"
                  }
                },
                {
                  "_icon": "textarea",
                  "text": "ceo",
                  "cls": "Wb.TextArea",
                  "properties": {
                    "cid": "ceo",
                    "text": "CEO",
                    "gridColumn": "span 3",
                    "height": "5em"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: template.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Use Template",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "tplTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tplTitle",
            "title": "HTML template"
          }
        },
        {
          "_icon": "component",
          "text": "tplComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "tplComp",
            "frame": "true",
            "tpl": "<div class='w-row w-gap w-lh w-justify-between w-padding'>\n  <div class='w-frame w-padding'>\n    <span class='w-active-color w-bold w-size1d2'>{p1}</span>\n    <br><span>Growth</span>\n  </div>\n  <div class='w-frame w-padding'>\n    <span class='w-active-color w-bold w-size1d2'>{[data.p2.usd]}</span>\n    <br><span>Income</span>\n  </div>\n  <div class='w-frame w-padding'>\n    <span class='w-active-color w-bold w-size1d2'>{p3}</span>\n    <br><span>Market</span>\n  </div>\n  <div class='w-frame w-padding'>\n    <span class='w-active-color w-bold w-size1d2'>{[data.p4.timeText12]}</span>\n    <br><span>Datetime</span>\n  </div>\n  <div class='w-frame w-padding'>\n    <span class='w-active-color w-bold w-size1d2'>{[data.p5.intText]}</span>\n    <br><span>Total</span>\n  </div>\n</div>",
            "cls": "w-fit",
            "textSelectable": "false",
            "tplData": "({ p1: '68.5%', p2: 325892, p3: 'Geejing', p4: new Date(), p5: 83236 })"
          }
        },
        {
          "_icon": "button",
          "text": "updateTplBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "updateTplBtn",
            "text": "Update template data",
            "type": "primary"
          },
          "events": {
            "click": "app.tplComp.update({ p1: '56.82%', p2: 839213, p4: new Date() });"
          }
        },
        {
          "_icon": "title",
          "text": "useViewTplTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "useViewTplTitle",
            "title": "Use view template"
          }
        },
        {
          "_icon": "list-view",
          "text": "tplView",
          "cls": "Wb.View",
          "properties": {
            "cid": "tplView",
            "html": "<div class='w-items w-row w-gap w-lh w-justify-between w-padding'>\n</div>",
            "data": "[\n  { name: 'Growth', value: '68.5%' },\n  { name: 'Income', value: (325892).usd },\n  { name: 'Market', value: 'Geejing' },\n  { name: 'Datetime', value: new Date().timeText12 },\n  { name: 'Total', value: (83236).intText }\n]",
            "itemTpl": "<div class='w-frame w-padding'>\n  <span class='w-active-color w-bold w-size1d2'>{value}</span>\n  <br><span>{name}</span>\n</div>",
            "selectColor": "none"
          }
        },
        {
          "_icon": "button",
          "text": "updateViewBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "updateViewBtn",
            "text": "Update view data",
            "type": "primary"
          },
          "events": {
            "click": "app.tplView.data = [\n  { name: 'Growth', value: '32.5%' },\n  { name: 'Income', value: (48372).usd },\n  { name: 'Datetime', value: new Date().timeText12 }\n]"
          }
        }
      ]
    }
  ]
}

File name: visual-design.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "items": [
        {
          "_icon": "text",
          "text": "text1",
          "cls": "Wb.Text",
          "properties": {
            "cid": "text1",
            "text": "text1"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number1",
          "cls": "Wb.Number",
          "properties": {
            "cid": "number1",
            "text": "number1"
          }
        },
        {
          "_icon": "button",
          "text": "button1",
          "cls": "Wb.Button",
          "_expanded": false,
          "properties": {
            "cid": "button1",
            "text": "button"
          }
        },
        {
          "_icon": "label",
          "text": "label1",
          "cls": "Wb.Label",
          "properties": {
            "cid": "label1",
            "html": "<ol>\n  <li>Select container control that requires visual design, such as click viewport1 node.</li>\n  <li>Click the [UI Designer] button on the toolbar to launch the visual layout designer.</li>\n  <li>Drag the control(or double click/Ctrl + double click) from the control tree to the designer.</li>\n  <li>Drag the control to adjust its position.</li>\n</ol>"
          }
        }
      ]
    }
  ]
}

File name: vue-el.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  'https://unpkg.com/element-ui/lib/theme-chalk/index.css',\n  'https://unpkg.com/vue@2/dist/vue.js',\n  'https://unpkg.com/element-ui/lib/index.js'\n]"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Cls['My.Select'] = class mySelect extends Wb.Control {\n  /** @property {Element} selectEl Select dom element. */\n  /** @property {Object} vue Vue object. */\n  /***/\n  init(configs) {\n    let me = this;\n    super.init(configs);\n    me.selectEl = me.addEl('w-flex w-fit').addEl();\n    me.vue = new Vue({\n      el: me.selectEl,\n      template: `<el-select v-model=\"value\" ref=\"select\" placeholder=\"Select\" @visible-change=\"handleVisibleChange\">\n                    <el-option\n                      v-for=\"item in items\"\n                      :key=\"item.value\"\n                      :label=\"item.text\"\n                      :value=\"item.value\">\n                    </el-option>\n                 </el-select>`,\n      data: {\n        multiple: false,\n        clearable: false,\n        items: [],\n        value: ''\n      },\n      methods: {\n        handleVisibleChange(visible) {\n          if (visible) {\n            // ElementUI PopupManager may read zindex from Globals.zIndex++\n            setTimeout(f => this.$refs.select.popperElm.style.zIndex = Globals.zIndex++);\n          }\n        }\n      }\n    });\n  }\n  /***/\n  destroy() {\n    super.destroy();\n    this.vue.$destroy();\n  }\n  /** @property {String} - The value of the select control. */\n  set value(value) {\n    this.vue.value = value;\n  }\n  /***/\n  get value() {\n    return this.vue.value;\n  }\n  /** @property {Array} - Dropdown option list. @key */\n  set items(value) {\n    this.vue.items = value;\n  }\n  /***/\n  get items() {\n    return this.vue.items;\n  }\n  /** @property {Boolean} - Whether to include a clear button. */\n  set clearable(value) {\n    this.vue.clearable = value;\n  }\n  /***/\n  get clearable() {\n    return this.vue.clearable;\n  }\n}"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Use Vue Element UI",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "label",
          "text": "descLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "descLabel",
            "text": "This example demonstrates how to use third-party components, such as vue element UI. We strongly recommend  that you use WebBuilder components, this example is only for demonstration purposes."
          }
        },
        {
          "_icon": "title",
          "text": "tplTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tplTitle",
            "title": "Element Select"
          }
        },
        {
          "_icon": "component",
          "text": "selectComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "selectComp",
            "cls": "w-fit"
          },
          "events": {
            "ready": "let comp = this;\n\ncomp.vue = new Vue({\n  el: comp.el.addEl(),\n  template: `<el-select v-model=\"value\" placeholder=\"Select\">\n                <el-option\n                  v-for=\"item in options\"\n                  :key=\"item.value\"\n                  :label=\"item.label\"\n                  :value=\"item.value\">\n                </el-option>\n             </el-select>`,\n  data() {\n    return {\n      options: [{\n        value: 'option1',\n        label: 'Golden Cake'\n      }, {\n        value: 'option2',\n        label: 'Chocolate'\n      }, {\n        value: 'option3',\n        label: 'Cookie'\n      }, {\n        value: 'option4',\n        label: 'Noodles'\n      }, {\n        value: 'option5',\n        label: 'Beef'\n      }],\n      value: ''\n    }\n  }\n});"
          }
        },
        {
          "_icon": "button",
          "text": "setValueBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "setValueBtn",
            "text": "Set Value",
            "justifySelf": "start"
          },
          "events": {
            "click": "app.selectComp.vue.value = 'option4';"
          }
        },
        {
          "_icon": "button",
          "text": "getValueBtn",
          "cls": "Wb.Button",
          "_expanded": true,
          "properties": {
            "cid": "getValueBtn",
            "text": "Get Value",
            "justifySelf": "start"
          },
          "events": {
            "click": "let vue = app.selectComp.vue, value = vue.value;\nWb.tip('Select value is: ' + vue.options.find(opt => opt.value == value)?.label);"
          }
        },
        {
          "_icon": "title",
          "text": "encapsulateToClassTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "encapsulateToClassTitle",
            "title": "Encapsulate into a class as a WebBuilder control"
          }
        },
        {
          "_icon": "button",
          "text": "addSelectBtn",
          "cls": "Wb.Button",
          "_expanded": true,
          "properties": {
            "cid": "addSelectBtn",
            "text": "Add Select Component",
            "justifySelf": "start"
          },
          "events": {
            "click": "let comp;\n\napp.selectIndex ??= 1;\ncomp = app.selectContainer.add({\n  cname: 'mySelect',\n  text: 'Select ' + app.selectIndex,\n  value: 'value2',\n  clearable: true,\n  items: [{\n    value: 'value1',\n    text: 'Golden Cake'\n  }, {\n    value: 'value2',\n    text: 'Chocolate'\n  }, {\n    value: 'value3',\n    text: 'Cookie'\n  }]\n});\ncomp.focus();\ncomp.highlight();\napp.selectIndex++;"
          }
        },
        {
          "_icon": "container",
          "text": "selectContainer",
          "cls": "Wb.Container",
          "properties": {
            "cid": "selectContainer",
            "layout": "grid2",
            "height": "10em"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "sourceLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sourceLabel",
            "text": "Source code of the class"
          }
        },
        {
          "_icon": "edit",
          "text": "codeEditor1",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "codeEditor1",
            "value": "// Defined in module.initialize event\nCls['My.Select'] = class mySelect extends Wb.Control {\n  /** @property {Element} selectEl Select dom element. */\n  /** @property {Object} vue Vue object. */\n  /***/\n  init(configs) {\n    let me = this;\n    super.init(configs);\n    me.selectEl = me.addEl('w-flex w-fit').addEl();\n    me.vue = new Vue({\n      el: me.selectEl,\n      template: `<el-select v-model=\"value\" ref=\"select\" placeholder=\"Select\" @visible-change=\"handleVisibleChange\">\n                    <el-option\n                      v-for=\"item in items\"\n                      :key=\"item.value\"\n                      :label=\"item.text\"\n                      :value=\"item.value\">\n                    </el-option>\n                 </el-select>`,\n      data: {\n        multiple: false,\n        clearable: false,\n        items: [],\n        value: ''\n      },\n      methods: {\n        handleVisibleChange(visible) {\n          if (visible) {\n            // ElementUI PopupManager may read zindex from Globals.zIndex++\n            setTimeout(f => this.$refs.select.popperElm.style.zIndex = Globals.zIndex++);\n          }\n        }\n      }\n    });\n  }\n  /***/\n  destroy() {\n    super.destroy();\n    this.vue.$destroy();\n  }\n  /** @property {String} - The value of the select control. */\n  set value(value) {\n    this.vue.value = value;\n  }\n  /***/\n  get value() {\n    return this.vue.value;\n  }\n  /** @property {Array} - Dropdown option list. @key */\n  set items(value) {\n    this.vue.items = value;\n  }\n  /***/\n  get items() {\n    return this.vue.items;\n  }\n  /** @property {Boolean} - Whether to include a clear button. */\n  set clearable(value) {\n    this.vue.clearable = value;\n  }\n  /***/\n  get clearable() {\n    return this.vue.clearable;\n  }\n}",
            "readonly": "true",
            "height": "25em"
          },
          "_expanded": true
        }
      ]
    }
  ]
}

File name: wizard.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Set buttons disabled.\n   */\n  setButtons() {\n    let ct = app.cardCt1, activeIndex = ct.activeIndex;\n\n    app.prevBtn.disabled = activeIndex == 0;\n    app.nextBtn.disabled = activeIndex >= ct.items.length - 1;\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "middle",
        "autoScroll": "true",
        "background": "true"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "win-restore",
          "text": "cardCt1",
          "cls": "Wb.CardCt",
          "properties": {
            "cid": "cardCt1",
            "buttonAlign": "center",
            "width": "40em",
            "height": "20em",
            "title": "Application wizard",
            "icon": "database",
            "padding": "true",
            "minButtonWidth": "8em",
            "frame": "true",
            "cls": "w-limit-width"
          },
          "events": {
            "ready": "app.setButtons();"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "container1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container1",
                "text": "Step1"
              }
            },
            {
              "_icon": "container",
              "text": "container2",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container2",
                "text": "Step2"
              }
            },
            {
              "_icon": "container",
              "text": "container3",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container3",
                "text": "Step3"
              }
            },
            {
              "_icon": "container",
              "text": "container4",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container4",
                "text": "Step4"
              }
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "buttons"
              },
              "text": "buttons",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Button",
                  "events": {
                    "click": "app.cardCt1.activeIndex--;\napp.setButtons();"
                  },
                  "properties": {
                    "cid": "prevBtn",
                    "text": "Previous",
                    "icon": "left4"
                  },
                  "text": "prevBtn",
                  "_expanded": true,
                  "_icon": "button"
                },
                {
                  "cls": "Wb.Button",
                  "events": {
                    "click": "app.cardCt1.activeIndex++;\napp.setButtons();"
                  },
                  "properties": {
                    "cid": "nextBtn",
                    "text": "Next",
                    "icon": "right4",
                    "iconAlign": "right"
                  },
                  "text": "nextBtn",
                  "_expanded": true,
                  "_icon": "button"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: common-usage.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates the general usage of client JavaScript programming\n//For more resources, please visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript\n\n//Open/Run modules, open windows and submit forms\nWb.open('m?xwl=admin/dbe'); //Open the database explorer module\nWb.open({ url: 'dbe', params: { foo: 'bar' }, method: 'POST' }); //Pass parameters and set method\nWb.openNormal('dbe'); //Open the database explorer module and allow the page to refresh\nWb.run({ url: 'dbe', params: { foo: 'bar' }, container: app.panel1 }); //Runs the module and displays it in the specified container\n//Run the module and dynamically add it's viewport1 to panel1 in the callback method. Owner means that viewport1 in dbe owns the\n//entire module, and destroys the entire module when viewport1 is destroyed\nWb.run({ url: 'dbe', owner: 'viewport1', success(scope) { app.panel1.add(scope.viewport1) } });\nWb.openWin('https://developer.mozilla.org/'); //Opens MDN in a new window\n//Set the parameters and method and open into the specified iframe\nWb.openWin({ url: 'dbe', params: { foo: 'bar' }, method: 'POST', target: 'myIframe' });\n//Submit the form with parameters\nWb.submit('https://www.geejing.com', { p1: 'foo', p2: new Date() });\n//Browse URL in Tab card(Wb.Tab.mainTab)\nWb.browse('https://www.geejing.com');\n\n//Download\nWb.download('m?xwl=foo/bar', { param1: 'abc' }); //Download url with parameters\nWb.downloadBlob(file1, 'myFile.txt'); //Download blob\nWb.downloadBase64(base64Text, 'myFile.dat'); //Download the Base64-encoded string as a file\n\n//Displays a dialog box containing the specified controls\nWb.prompt('My Dialog', [{ text: 'Your Name', cid: 'name' }], (values, win) => {\n  Wb.tip(values.name);\n  win.close();\n});\n\n//Show message\nWb.info('Hello'); //Show info\nWb.info('<span style=\"color:red\">Hello</span>', f => { Wb.tip('click ok') }, true); //Show HTML info\nWb.succ('Hello'); //Show succ\nWb.warn('Hello'); //Show warn\nWb.error('Hello'); //Show error\n//Displays a confirmation dialog box with OK and Cancel buttons\nWb.confirm('Are you sure you want to delete?', f => doSomething());\n//Displays a confirmation dialog box with Yes, No, and Cancel buttons\nWb.choose('Are you sure you want to save?', button => Wb.info(button));\nWb.tip('Hello'); //Show tip that is automatically close with a delay\nWb.tip('<span style=\"color:red\">Hello</span>', true); //Show HTML tip\nWb.tipAt('Hello', 200, 30); //Show tip at the specified location\nWb.tipSucc('Hello'); //Show succ tip\nWb.tipWarn('Hello'); //Show warn tip\nWb.tipError('Hello'); //Show error tip\nWb.toast('Hello'); //Show single tip that is automatically close with a delay in the center of the window\nWb.toast('<span style=\"color:red\">Hello</span>', true); //Show HTML toast\nWb.toastSucc('Hello'); //Show succ toast\nWb.toastWarn('Hello'); //Show warn succ\nWb.toastError('Hello'); //Show error succ\nWb.tipSelect(); //Show \"Please select a valid item\" tip in the local language\n\n//Load JS/MJS/CSS files\nWb.load('foo/bar.js', f => doSomething()); //Load js file\n//Load CSS/JS in a specific way\nWb.load(['file.css', 'file.js', { url: 'foo/bar', async: true, type: 'js', reload: true, autoRemove: true }], callback);\nWb.loadx('foo/bar.js').then(f => doSomething()); //Promise version of Wb.load\nWb.loadModule('wb/js/my.mjs', module => module.MyCls.doSomething()); //Load mjs module file\n\n//Access elements as components\nWb.get(div).tip = 'message'; //Gets the Wb.Component object of the specified element and sets the property\nWb.fly('my-div').hide(); //Gets a shared Wb.Component object for the specified element and makes a method call\n\n//Access, setting, and validation values\nvalues = Wb.getValue(panel1); //Get the values of all components in panel1\nvalues = Wb.getValue([panel1, viewport1], true); //Get the values of all components in panel1, viewport1\nWb.setValue(container1, { text1: 'foo', number1: 123 }); //Set the values in container1\n///Set the values to the specific component in container1, panel1\nWb.setValue([container1, panel1], { text1: 'foo', number1: 123 }, true, true);\nWb.reset(panel1); //Resets the values of all components in panel1\nWb.reset([panel1, text1], true); //Resets the values of specific components in panel1 and text1\nWb.clear(panel1); //Clears the values of all components in panel1\nWb.clear([panel1, text1], true); //Clears the values of specific components in panel1 and text1\nif (Wb.verify(panel1)) return; //Verify the values of all components in panel1\nWb.verify([panel1, date1], true, true);  //Verify the values of specific components in panel1 and date1\n\n//Send requests\n//Send GET request with parameters\nWb.ajax({ url: path, params: { p1: 'abc', p2: 123 }, method: 'GET', success(resp) { doSuccess(); } });\n//Send POST request with parameters, file and blob\nWb.ajax({ url: path, params: { p1: 'abc', p2: 123, p3: file, p4: blob } });\n//Send request with form, comps and params\nWb.ajax({ url: path, form: myForm, comps: [panel1, file1], params: { foo: 'bar' } });\n//Send request with object and array parameters\nWb.ajax({ url: path, params: { object: { foo: 'bar' }, array: [1, 'abc', new Date()] } });\n//Send request with multiple parameters with the same name\nWb.ajax({ url: path, params: { myParam: Wb.markParams([1, 'abc', new Date()]) } });\n//Send request with payload data and specific header\nWb.ajax({ url: path, data: bigDataContent, header: { 'Content-Type': 'text/html;charset=utf-8' } });\n//Download file in ajax mode\nWb.ajax({ url: path, download: true });\n//Send request with object that can be auto deserialized in server side\nWb.ajax({ url: path, object: { foo: 'bar', num: 123, date: new Date() } });\n//Promise version of Wb.ajax\nWb.fetch({ url: path, params: { p1: 'abc', p2: 123 } }).then(\n  result => {\n    if (result.ok)\n      console.log(result.response);\n    else\n      console.warn('failed');\n  }\n);\n\n//Set the style and class of documentElement\nWb.addCls('w-cls'); //Add class\nWb.removeCls('w-cls'); //Remove class\nWb.setCls(true, 'w-cls'); //Add or remove class\nWb.toggleCls('w-cls'); //Toggle class\n\n//Some getter properties of Wb\nWb.activeComp; //The component that currently has focus\nWb.activeWindow; //The window that currently has focus\n\n//The properties and methods of Event\ne.stopEvent(); //Prevents default and stops the propagation of event\ne.stopSpread(); //Alias for Event.prototype.stopImmediatePropagation\ne.hasAssistKey; //Whether to press any of the Ctrl, Shift, Alt, or Meta keys\n\n//The methods of Function\nWb.info.delay(Wb, 1000, 'Hello'); //Show info with delay\nWb.info.delay(Wb, 1000, 'Hello World'); //Cancel pending delay and show info with delay again\nWb.warn.delays(Wb, 1000, 'Hello'); //Show warn with delay without cancel pending delay\nWb.tip.delay(Wb, 1000, 'tip'); //Show tip with delay\nWb.tip.cancelDelay(Wb); //Cancel pending tip immediately",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: control.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "wrapBorder": "0",
        "readonly": "true",
        "value": "//Access components and controls\n\n//General properties and functions\nlet text = control.text; //Get label text of the control\ncontrol.text = 'text label'; //Text label\ncontrol.html = 'html label'; //HTML label, this property is invalid after setting text property\ncontrol.width = '10em'; //Set width\ncontrol.height = 80; //Set height\ncontrol.visible = false; //Hide control\ncontrol.hide(); //Same as above\ncontrol?.hide(); //Call hide with optional chaining\ncontrol.visible = true; //Show control\ncontrol.show(); //Same as above\ncontrol.appeared = false; //Hide control(not drawn), but still affects layout as normal.\ncontrol.setAppeared(false); //Same as above\ncontrol.disabled = true; //Disable control\ncontrol.setDisabled(true); //Same as above\ncontrol.icon = 'paste'; //Set icon\ncontrol.el.cls = 'cls1 cls2'; //Add cls1, cls2 class name to control's element\ncontrol.cls = 'cls1 cls2'; //Same as above\nconrols.el.removeCls('cls1'); //Remove control's element class name\nconrols.removeCls('cls1'); //Same as above\ncontrol.el.setStyle('color', 'red'); //Set control's element style\ncontrol.setStyle('color', 'red'); //Same as above\ncontrol.el.removeStyle('color'); //Remove control's element style\ncontrol.removeStyle('color'); //Same as above\n\n//Add and delete controls\npanel.insertBefore({ cname: 'text' }, text1); //Insert a new text box before text1\npanel.insertAfter({ cname: 'text' }, text1); //Insert a new text box after text1\npanel.insert(2, { cname: 'text' }); //Insert a new text box at the specific index\npanel.add({ cname: 'button', text: 'My Button' }); //Add components at the last\npanel.addHeader({ cname: 'text' }); //Add a text box as a sub-component in the panel header\npanel.addFooter({ cname: 'text' }); //Add a text box as a sub-component in the panel footer\ncontrol.remove(); //Remove control (control is reusable)\npanel.removeAll(); //Remove all child components in panel (components are reusable)\ncontrol.destroy(); //Destroy control (Really delete)\npanel.destroyAll(); //Destroy all child components in panel (Really delete)\n\n//Event and DOM listeners\npanel.on('destroy', app.onDestroy); //Add component event\npanel.un('destroy', app.onDestroy); //Remove component event\npanel.mon('keydown', app.onKeyDown); //Add dom listeners to panel's element\npanel.mun('keydown', app.onKeyDown); //Remove dom listeners from panel's element\npanel.mon(DocEl, 'keydown', app.onKeyDown); //Add dom listeners to DocEl, the listener will be removed after panel is destroyed.\npanel.mun(DocEl, 'keydown', app.onKeyDown); //Remove dom listeners from DocEl\n\n//Traverse and find\ncontrol = app.myControl; //Down control by app\ncontrol = panel.find('nameText'); //Find direct control by cid\ncontrol = panel.find(item => item.visible && item.foo == 'bar'); //Find direct control by function\ncontrol = panel.down('myControl'); //Down control by cid\ncontrol = panel.down(item => item.visible && item.foo == 'bar'); //Down control by function\ncontrol = panel.children.myControl; //Down control by proxy \"children\"\npanel = text.up('myPanel'); //Up control by cid\npanel = text.up(item => item.visible && item.foo == 'bar'); //Up control by function\ncomps = panel.filter(fn); //Find all direct comps by function\ncomps = panel.downAll(fn); //Down all comps by function\ncomps = text.upAll(fn); //Up all comps by function\npanel.each(item => console.log(item)); // Each direct child comps\npanel.eachOwned(item => console.log(item)); // Each sub-comps\npanel.eachWhole(item => console.log(item)); //Each direct child comps and sub-comps\npanel.cascade(item => console.log(item)); //Cascade comps\npanel.bubble(item => console.log(item)); //Bubble comps\ncontrol = comp.nextSibling; //Gets comp's next sibling\ncontrol = comp.previousSibling; //Gets comp's previous sibling"
      }
    }
  ]
}

File name: dom-element.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "wrapBorder": "0",
        "readonly": "true",
        "value": "//This example demonstrates dom elements. Only extended members are demonstrated, and native members are described at:\n//https://developer.mozilla.org/en-US/docs/Web/API/Element\n\n//Associations between DOM elements and components\nlet buttonEl = button1.el; //Gets the DOM element associated with the button1\nlet windowEl = window1.el; //Gets the DOM element associated with the window1\nlet button1 = buttonEl.citem; //Gets the button component associated with the button element\nlet window1 = windowEl.citem; //Gets the window component associated with the window element\nlet grid = div.getClosestComp(Wb.Grid); //Get the closest Wb.Grid component that contains div\n\n//Add, delete and modify\nnewEl = parentEl.insertEl(3, 'my-class', 'span'); //Creates a new element and inserts it before the specified index element\nnewEl = parentEl.addEl('my-class'); ///Creates a new element and adds it to the last\nnewEl = parentEl.addTag('span'); //Creates a new element with specific tag and adds it to the last\nnewEl = parentEl.insertAfter(el, refEl); //Inserts a node after the specified node\nnewEl = parentEl.insertElBefore('my-class', 'span', refNode); //Creates a new element and inserts it before the specified node\nnewEl = parentEl.insertElAfter('my-class', 'span', refNode); //Creates a new element and inserts it after the specified node\ndiv.insertHtml('afterend', '<div>abc</div>') //insert HTML, alias for insertAdjacentHTML\ndiv.clearChildren(); //Empty all child nodes in the div\n\n//Element class name\nlet cls = div.cls; //Gets class name of div\ndiv.cls = 'cls1'; //Add new class name cls1 to div. Keep the old classNames.\ndiv.cls = 'cls1 cls2 cls3'; //Add new classNames cls1, cls2, cls3 to div. Keep the old classNames.\nel.addCls('my-cls'); //Add a single class name to el\nel.removeCls('my-cls'); //Remove a single class name from el\nel.removeClasses('cls1 cls2 cls3'); ///Remove multiple class names from el\nel.setCls(true, 'my-cls'); //Add or remove a single class name\nel.toggleCls('my-cls'); //Toggle a single class name\nel.containsCls('my-cls'); //Determines whether to include a class name\nel.plainIcon = true; //All icons in this element use a uniform icon color\nel.layout = 'grid3'; //Setting the layout, unlike setting the class, setting a new layout will remove the original layout\n\n//Element style\nel.setStyle('color: red'); //Set element style\nel.getStyle('color'); //Gets element style\nel.removeStyle('color'); //Remove element style\nel.computeStyle(); //Returns a list of calculated styles\nel.styleText = 'color: red; width: 10em'; //Add styles separated by semicolons to an element. Keep the old styles.\n\n//Traverse elements\ndiv.each((el, index) => console.log(el, index)); //Iterate direct child elements\ndiv.bubble(el => console.log(el)); //Bubble traverse\ndiv.bubbleParent(fn); //Bubble traverse without div self\ndiv.cascade(el => console.log(el)); //Cascade traverse\ndiv.cascadeSelf(fn); //Cascade traverse with div self\ndiv.some(el => el.cls.includes('w-disp-none')); //Determine whether there is a specific direct child element\ndiv.every(el => el.cls.includes('w-disp-none')); //Determines whether all direct child elements pass the test function\ndiv.cascadeSome(el => el.cls.includes('w-disp-none')); //Determine whether there is a specific child element\ndiv.cascadeEvery(el => el.cls.includes('w-disp-none')); //Determines whether all child elements pass the test function\ndiv.bubbleSome(el => el.cls.includes('w-disp-none')); //Determine whether there is a specific parent element\ndiv.bubbleEvery(el => el.cls.includes('w-disp-none')); //Determines whether all parent elements pass the test function\n\n//Find elements\nel = div.query('div.w-row'); //Down query the first element, alias for querySelector\nelList = div.queryAll('div.w-row'); //Down query all elements, alias for querySelectorAll\nel = div.closest('div.w-row'); //Match the closest parent element\nel = div.find('w-row'); //Down query the first child element with the specific class name\nel = div.findParent('w-row'); //Up query the first parent element with the specific class name\nelList = div.findParents('w-row'); //Up query all parent elements with the specific class name\nelList = div.filter('w-row'); //Down query all child elements with the specific class name, alias for getElementsByClassName.\nel = div.down('div.w-row'); //Down query the first element, similar to query\nelList = div.downAll(selectors); //Down query all elements, similar to queryAll\nel.downBy(fn, scope); //Down query the first element by function\nel.downAllBy(fn, scope); //Down query all elements by function\nel = div.up('div.w-row'); //Up query the first element, similar to closest\nelList = div.upAll(selectors); //Up query all elements\nel.upBy(fn, scope); //Up query the first element by function\nel.upAllBy(fn, scope); //Up query all elements by function\n\n//Animation effects\ndiv.fadeIn(); //Displays a fade-in effect\ndiv.fadeIn(e => fn(), 300); //Displays a fade-in effect for the specified duration. Execute the callback function when finished.\nWb.info('ok').el.fadeIn(); //Displays a info dialog with the fade-in effect\ndiv.fadeOut(); //Displays a fade-out effect\nwindow1.el.fadeOut(e => window1.close()); //Close the window after displaying the fade-out effect\ndiv.slideIn(); //Slide in from the left\ndiv.slideIn(true); //Slide in from the top\n//Slide in from {x: '10px', y: '5em'} for 2 seconds, and finally remove div\ndiv.slideIn({ x: 10, y: '5em', callback() { div.remove() }, duration: 2000, easing: 'ease-in-out' });\nWb.error('error').el.slideIn(); //Displays an error dialog with the slide-in effect\nel.slideOut(); //Slide out from the right side\nel.slideOut(true); //Slide out from the bottom\nSlide to {x: '10px', y: '5em'} for 2 seconds, and finally remove div\ndiv.slideOut({ x: 10, y: '5em', callback() { div.remove() }, duration: 2000, easing: 'ease-in-out' });\nwindow1.el.slideOut({ callback() { window1.close() } }) //Close the window after displaying the slide-out effect\ndiv.slideTo({ x: 10, y: '2em' }) //The element slides from the current position to the specified position\ndiv.rippleEffect(100, 200); //Generates a ripple effect at a specific location\ndiv.highlight(); //Highlight effect\ndiv.highlight('#f00'); //Highlight effect with the specific color\n\n//Size, location, appearance etc.\nrect = div.getRect(); //Gets the rect of the element, alias for getBoundingClientRect\ndiv.setRect({ x, y, width, height }); //Set the rect of the element\nel.alignTo(destEl, \"tl-bl?\"); // Aligns the element\nel.alignTo(destEl, \"bl-c\", [-8, 0]);// Aligns the element with offsets\ndiv.visible = false; //Turns off the display of an element so that it has no effect on layout.\ndiv.setVisible(false); //Same as above\ndiv.appeared = false; //The element is invisible (not drawn), but still affects layout as normal.\ndiv.setAppeared(false); //Same as above\nisVisible = div.isVisible(); //Determines whether the element is visible deeply\ndiv.show(); //Show element, same as visible = true\ndiv.hide(); //Hide element, same as visible = false\ndiv.width = '8em'; //Set width\ndiv.setWidth('8em'); //Same as above\ndiv.height = 90; //Set height\ndiv.setHeight(90); //Same as above\ndiv.x = '8em'; //Set left\ndiv.setX('8em'); //Same as above\ndiv.y = 90; //Set top\ndiv.setY(90); //Same as above\ndiv.xy = ['8em', 90]; //Set left and top\ndiv.intoView(); //Scroll div into view\ndiv.intoViewCenter(); ///Scroll div into view center\ndiv.focusOnly(); //Sets the focus on div without adjusting the scroll bar\ndiv.autoScroll = true; //Whether scrolling is allowed"
      }
    }
  ]
}

File name: array-set.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates array and set. Only extended members are demonstrated, and native members are described at:\n//Array: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n//Set: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set\n\n//Add, delete and modify\narray = [1, 2];\narray.push(3); //push new item\narray.pushAll([1, 3, 5]); //All items are added to the array, output: [1, 2, 3, 1, 3, 5]\n[1, 2, 3].merge(new Set([3, 4, 5])); //array merge array/set, output: [1, 2, 3, 3, 4, 5]\narray.remove(2); //remove the first item\narray.removeAll(3); //remove all items\narray.removeFirst(item => item === 2); //remove the first item by function\narray.removeBy(item => item === 2); //remove all items by function\narray.erase(2); //remove item by item index\n[1, 2, 3].insert(0, 'a', 'b'); //Insert items at the specified index, output: [\"a\", \"b\", 1, 2, 3]\narray.pushIf(null, 'b'); //Push values without null/undefined value\n\n//Traverse\n[1, 2, 3].forEach((item, index) => console.log(item));\n[1, 2, 3].each((item, index) => { //each is similar to foreach, each can be interrupted and reversed\n  console.log(item);\n  if (item == 2)\n    return false; //interrupted\n}, scope, true); //true means reverse traversal\n(new Set()).forEach(fn); //forEach set\n(new Set()).each(item => { console.log(item) }); //each set\n\n//Sort\n[1, 2, 3].sort();\narray = [123, 'abc', 'utf-string'];\narray.normalSort(); //Local sort\narray = [{ a: 3, b: 2 }, { a: 1, b: 4 }];\narray.normalSort('a'); //Sort by the \"a\" property\narray.normalSort(['a', 'b'], [false, true]); //Sort by the \"a\" property asc, \"b\" property desc.\narray.lowerSort('a'); //Similar to normalSort, case insensitive.\narray.mixSort('b'); //Similar to normalSort, Mix sort string, number and date locally in case-insensitive. Recommended sorting method.\n(new Set()).sort(params);\n(new Set()).normalSort(params);\n(new Set()).lowerSort(params);\n(new Set()).mixSort(params);\n\n//Prototype function\n[{ name: 'Allen' }, { name: 'Linda' }].pluck('name'); //Extracts the specified name attribute value, output: [\"Allen\", \"Linda\"]\n[1, 3, 5].diff([3, 5]); //Get difference, output: [1]\n[1, 3, 5].intersect([3, 5]); //Get intersect, output: [3,5]\n[1, { foo: 'bar' }, 5].copy(); //Shallow copy\n[1, 3, 5].equals([1, 3, 5]); //Determine whether the content is the same\n[1, 2, 1, 3].unique(); //Distinct array, output: [1, 2, 3]\n[1, 2, 3].unique([3, 4, 5]); //Distinct array, output:[1, 2, 3, 4, 5]\n['a', 'b', 'c'].unique(['d', 'b'], true); //Distinct array with subtract, output: ['a', 'c', 'd', 'b']\nindex = array.indexOf(2); //Get the index of item\nincludes = array.includes(2); //Determines whether the specified item is included\n\n//Getter properties\n[1, 2, 3].firstItem; //Return the first item\n[1, 2, 3].lastItem; //Return the last item",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: common-usage.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates the general usage of JavaScript in client and server programming\n//For more resources, please visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript\n\n//Define a class\nCls['My.pack.ClassName'] = class myClassName extends Wb.Base {\n  //static class prototype property\n  static foo = 'bar';\n\n  //static class prototype function\n  static func() {\n  }\n\n  //instance property\n  myProp = 'xxx'\n\n  //private instance property\n  #privateProp = new Date();\n\n  //instance prototype members(all members of \"protos\" will be added to the prototype of the class instance)\n  static protos = {\n    foo: 'bar',\n    abc: 123\n  };\n\n  //constructor function\n  constructor(param) {\n    super(param); //call super function\n    this.param = param; //add param to the class instance\n    this.abc = 234; //Add class instance property abc\n    this.def = 456; //Add class instance property def\n    console.log(this.foo); //show prototype property foo, foo value is \"bar\"\n    console.log(this.abc); //show instance property abc, abc value is \"234\"\n    delete this.abc; //delete instance property abc\n    console.log(this.abc); //show prototype property abc, abc value is \"123\", because there is no instance property at this time\n    console.log(this.#privateProp); //show private instance property #privateProp\n    this.height = 456; //use setter\n    console.log(this.height); //use getter\n  }\n\n  //prototype function\n  myFunc(param) {\n    super.myFunc(param); //call super function\n  }\n\n  //Setter\n  set height(value) {\n    let me = this;\n\n    if (me.height$ !== value) {\n      me.height$ = value;\n      me.doSomething();\n    }\n  }\n\n  //Getter\n  get height() {\n    return this.height$;\n  }\n}\nlet myCls = new My.pack.ClassName(); //create a new instance\n\n//Define getter and setter in app\nWb.define(app, {\n  /** @property {String} - Setter description. */\n  set$name(value) {\n    //this is equal to app\n    if (this.name$ !== value) {\n      this.name$ = value;\n      doSomething();\n    }\n  },\n  /** @property {String} - Getter description. */\n  get$name() {\n    //this is equal to app\n    return this.name$;\n  },\n  /**\n   * Function description.\n   */\n  myMethod() {\n  }\n});\n\n//Determines the type of value\nisString = Wb.isString(123); //Determines whether the value is a string\nisNumber = Wb.isNumber('abc'); //Determines whether the value is a number\nisNumeric = Wb.isNumeric('123'); //Determines whether the value is a numeric value\nisDate = Wb.isDate(123); //Determines whether the value is a date\nisBoolean = Wb.isBoolean('abc'); //Determines whether the value is a boolean\nisArray = Wb.isArray([1, 2, 3]); //Determines whether the value is an array\nisObject = Wb.isObject({ foo: 'bar' }); //Determines whether the value is a \"JavaScript Object\"\nisFunction = Wb.isFunction('abc'); ///Determines whether the value is a Function\nisPrimitive = Wb.isPrimitive(123); //Determine whether the value is of the narrow original type(String, Number, and Boolean)\nisIterable = Wb.isIterable([]); //Determines whether the value is traversable\nisEmpty = Wb.isEmpty({}); //Determine whether the value is empty(null, undefined, '', [], {} and other object with length 0)\n\n//Conversion of values\nstringValue = String(value); //to a string\nboolValue = Wb.parseBool('false'); //to a boolean. 'false', '0' and falsy value return false, others return true\ndateValue = Wb.parseDate('2021-3-10 15:13:12.18', 'y-MM-dd HH:mm:ss.S'); //Converts specified format text to date\nintValue = parseInt('123'); //to an integer\nfloatValue = parseFloat('1.23'); //to a float\n\n//Copies all enumerable own properties from one or more source objects to a target object. Alias for Object.assign.\nresult = Wb.apply({ abc: 123 }, { foo: 'bar' });\n//Copies non-existent properties from source objects\nresult = Wb.applyIf({ foo: 'abc' }, { foo: 'def', bar: 123 });\n//Copies non undefined/null properties from source objects\nresult = Wb.applyValue({ foo: 'abc' }, { bar: 123, more: null });\n//Copies properties from source objects with specified names\nresult = Wb.applyWith({ foo: 'abc' }, { param1: 'def', param2: 123 }, ['param2']);\n//Copies non undefined/null properties from source objects with specified names\nresult = Wb.applyValueWith({ foo: 'abc' }, { param1: 'def', param2: null }, ['param2']);\n//Copies properties from source objects without specified names\nresult = Wb.applyWithout({ foo: 'abc' }, { param1: 'def', param2: 123 }, ['param2']);\n//Copies properties from source objects and merge object\nresult = Wb.applyMerge({ a: { v1: 1, v2: 2 }, b: 'abc' }, { a: { v3: 3, v4: 4 }, c: 'xyz' });\n//Shallow copy object\nresult = Wb.copy({ abc: 123 });\n//Deep clone object\nresult = Wb.clone({ foo: 'bar', arr: [1, 2, 3] });\n//Copies object with getter/setter support\nresult = Wb.define({ get$method() { }, set$method() { } });\n\n//Encode and decode\n//Converts a JavaScript value to a JSON string. Alias for JSON.stringify.\nlet text = Wb.encode({ foo: 'bar', abc: 123 });\n//Parses a JSON string, constructing the JavaScript value or object described by the string. Alias for JSON.parse.\nlet object = Wb.decode(text);\n//Wb.encode with pretty formatting\nlet prettyText = Wb.encodePretty({ foo: 'bar' });\n//Serialize a JavaScript value to a string. The difference from Wb.encode is that it supports date encoding.\ntext = Wb.serialize({ foo: 'bar', date: new Date()});\n//Deserialize a string to a JavaScript value. The difference from Wb.decode is that it supports date decoding.\nobject = Wb.deserialize(text);\n//Encode the string to a Base64 string\nbase64Text = Wb.encodeBase64(text);\n//Decodes the Base64 string to a string\ntext = Wb.decodeBase64(base64Text);\n\n//Iterates through all members of the object\nWb.each({ abc: 123 }, (k, v) => {\n  console.log(k);\n  if (k == 'foo')\n    return false; //Break iteration\n});\n\n//Find object\n//Find the first member name and output \"bar\"\nWb.find({ foo: 123, bar: 'abc' }, (k, v) => v === 'abc');\n//Find all member names\nWb.filter(object, fn);\n//Determine whether some members meet test condition\nWb.some({ foo: { name: 'Allen' }, bar: { name: 'Linda' } }, (k, v) => v.name == 'Linda');\n//Determine whether all members meet test condition\nWb.every({ foo: { name: 'Allen' }, bar: { name: 'Linda' } }, (k, v) => v.name == 'Linda');\n//Finds the first member name in an object that has the specified value\nWb.findKey({ a: 123 }, 123);\n\n//Down traverse\nitems = [{ param: 'one' }, { param: 'two', items: [{ param: 'three' }] }];\nWb.cascade(items, item => console.log(item.param)); //output: one, two, three\nWb.cascade(parentNode, node => console.log(node), 'childNodes'); //Traverses all child nodes of the element\nobject = { param: 'two', items: [{ param: 'three' }] };\nitems = [{ param: 'one' }, object];\nWb.down(items, item => item.param === 'two'); //Down traverses the object and find the first item\nWb.downAll(items, fn); //Down traverses the object and find all items\n\n//Up traverse\nlet items = { param: 'one', parent: { param: 'two', parent: { param: 'three' } } };\nWb.bubble(items, parent => console.log(parent.param)); //output: one, two, three\nWb.bubble(childNode, parent => console.log(parent), 'parentNode'); //Traverses all parent nodes of an element\nlet object = { param: 'two', parent: { param: 'three' } };\nlet items = { param: 'one', parent: object };\nWb.up(items, parent => parent.param === 'two'); //Up traverses the object and find the first item\nWb.upAll(items, fn); //Up traverses the object and find all items\n\n//Exception handling\ntry {\n  doSomething();\n  Wb.raise('error message'); //throw and Error\n} catch (e) {\n  processException();\n  throw e;\n} finally {\n  finallyMethod();\n}\n\n//Value comparison\nWb.compare(a, b); //Intl.Collator().compare\nWb.lowerCompare(a, b); //to lowercase compare\nWb.mixCompare(a, b); //Mixed values compare, supports local comparison of UTF characters.\n\n//Value formatting\nval = Wb.format(1234, '#,##0.00'); //Format a number, output: \"1,234.00\"\nval = Wb.format('Your name is {name}', { name: 'Jeena' }); //Format a string, output: \"Your name is Jeena\"\nval = Wb.format('foo {0} bar {1} and {0}', 'abc', 123); //Format a string, output: \"foo abc bar 123 and abc\"\nval = Wb.format(new Date('2021-3-10'), 'y-MM-dd'); //Format a date, output: \"2021-03-10\"",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: date.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates date. Only extended members are demonstrated, and native members are described at:\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date\n\nlet date1 = new Date(), date2 = date1.addDay(3), date3 = date1.addDay(10);\n\n//Date add/subtract\ndate1.addMilliSec(3); //add milliseconds\ndate1.addMilliSec(-3); //Subtract milliseconds\ndate1.addSecond(3); //add seconds\ndate1.addMinute(3); //add minutes\ndate1.addHour(3); //add hours\ndate1.addDay(3); //add days\ndate1.addMonth(3); //add months\ndate1.addYear(3); //add years\n\n//Prototype functions\ndate1.equals(date2); //Determines whether the current date is equal to the specified date.\ndate1.equalsDate(date2); //Determines whether the current date is equal to the specified date, only compare date part.\ndate1.equalsMonth(date2); //Determines whether the current date is equal to the specified date, only compare year and month.\ndate2.between(date1, date3); //Determines whether the current date is between the specified start and end date.\ndate2.elapse(date1); //Gets the number of milliseconds from the current date to the specified date\n\n//Formatting\n//output like: \"2023-03-02 10:24:03.672\"\ndate1.format('y-MM-dd HH:mm:ss.S');\n//to date value in text, region independent.\ndate1.textValue;\n//equals to textValue property\ndate1.toString();\n//to local date text in 4-digit year and 2-digit month days, in some regions output: \"2021/03/09\"\ndate1.dateText;\n//to local time text in 24-hour 2-digit hours, minutes, and seconds, in some regions output: \"15:03:23\"\ndate1.timeText;\n//to local time text in 12-hour 2-digit hours, minutes, and seconds, in some regions output: \"PM 02:15:29\"\ndate1.timeText12;\n//to local date time text in 4-digit year and 2-digit month days, 24-hour 2-digit hours, minutes, and seconds,\n//in some regions output: \"2021/03/09 15:01:10\"\ndate1.dateTimeText;\n//to local date time text in 4-digit year and 2-digit month days, 14-hour 2-digit hours, minutes, and seconds,\n//in some regions output: \"2022/10/06 PM 02:17:06\"\ndate1.dateTimeText12;\n//similar to the dateTimeText property. If hours, minutes, seconds are 0, only the date part is displayed.\ndate1.autoText;\n//similar to the dateTimeText property, with 3-digit millisecond, in some regions output: \"2021/03/09 15:01:10.120\"\ndate1.dateTimeMilliText;\n//similar to the dateTimeText12 property, with 3-digit millisecond, in some regions output: \"2021/03/09 PM 02:17:06.120\"\ndate1.dateTimeMilliText12;\n\n//Getter properties\ndate1.isLeapYear; //Determine whether it is a leap year\ndate1.monthFirstDate; //Return the date of the first day of the month\ndate1.monthLastDate; //Return the date of the last day of the month\ndate1.monthDays; //Return the number of days in the month\ndate1.mondayDate; //Return the date value of the Monday of the week. Monday as the first day of the week.\ndate1.sundayDate; //Return the date value of the Sunday of the week. Sunday as the first day of the week.\ndate1.datePart; //Return the date part, the time part is set to 0.\ndate1.timePart; //Return the time part, the date part is set to \"1900-01-01\".",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: map.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates map. Only extended members are demonstrated, and native members are described at:\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map\n\n//Add, delete and modify\nlet map = new Map([['key1', 'Allen'], ['key2', 'Linda']]);\nmap.set('key', 'value'); //Add or modify\nmap.remove('key'); //Delete\n\n//Traverse\nmap.forEach((v, k, map) => console.log(v));\nmap.each((k, v) => { console.log(k, v) }); //each is similar to foreach, each can be interrupted\nmap.some((k, v) => v === 'Linda'); //Like Array.some\nmap.every((k, v) => v.includes('e')); //Like Array.every\nmap.find(k => k == 'key2'); //Like Array.find\nmap.filter((k, v) => v.includes('e')); //Like Array.filter",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: number.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates number. Only extended members are demonstrated, and native members are described at:\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number\n\n//General usage\n(92).snap(5); //Converts to the value closest to the specified step, output: 90\n(56).constrain(80, 90); //limit value >= minValue and value <= maxValue, output: 80\n\n//Formatting\n(12345.625).format('$#,##0.00'); //\"$12,345.63\"\n(12345.625).format('0.0###'); //\"12345.625\"\n(1.23).format('000'); //\"001\"\n(1.235).format('0.00%'); //\"1.24%\"\n(1234.5678).text; //to local text, in some regions output: \"1,234.568\"\n(1234.5678).intText; //to local integer text, in some regions output: \"1,235\"\n(1.2).floatText; //to local float text, in some regions output: \"1.2\"\n(1.2).percent; //to local percent text(2 decimal places), in some regions output: \"120.00%\"\n(1.2).percentInt; //to local percent text(no decimals), in some regions output: \"120%\"\n(1.2).percentAuto; //to local percent text(Keep up to 2 decimal places), in some regions output: \"120%\"\n\n//Currency\n(1234.5).usd; //to local USD currency text, in some regions output: \"US$1,234.50\"\n(1234.5).cny; //to local RMB currency text, in some regions output: \"¥1,234.50\"\n(1234.5).eur; //to local Euro currency text, in some regions output: \"€1,234.50\"\n(1234.5).gbp; //to local GBP currency text, in some regions output: \"£1,234.50\"\n(1234.5).jpy; //to local Yen currency text, in some regions output: \"JP¥1,235\"\n(1234.5).cad; //to local Cad currency text, in some regions output: \"CA$1,234.50\"\n(1234.5).aud; //to local Australian dollars currency text, in some regions output: \"AU$1,234.50\"\n(1234.5).hkd; //to local Hong Kong dollars currency text, in some regions output: \"HK$1,234.50\"\n\n//Byte size\n(12345678).kb; //to local KB size text, in some regions output: \"12,056 KB\"\n(12345678912).mb; //to local MB size text, in some regions output: \"11,774 MB\"\n(12345678912).fileSize; //to local MB/KB/B size text, in some regions output: \"11,774 MB\"\n\n//Getter properties\n(123.45).decimalCount; //Decimal place count\n(123.4).hasDecimal; //Whether include decimals",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: string.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates string. Only extended members are demonstrated, and native members are described at:\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String\n\n//General usage\n'my name is'.contains('name'); //Whether to contain \"name\", alias for String.includes\n'foo bar foo'.occur('foo'); //Gets the number of occurrences of the specified string\n'my name is: _$name$_'.replaceParams({ name: 'Allen' }); //Replaces the text using the _$name$_ syntax\n'<div>'.toHTML(true, true); //To HTML\n'abc, def, ghi'.splitTrim(); //Split the string and trim each item.\n\n//Formatting\n'foo {0} bar {1} and {0}'.format('abc', 123);\n'foo {value1} bar {value2}'.format({ value1: 'abc', value2: 123 });\n'23508172639'.formatGroup('???? ???? ???'); //Group formatting, output: \"2350 8172 639\"\n'abc def'.formatGroup('??-?? ?? ??'); //Group formatting, output: \"ab-cd ef\"\n'abc def'.formatGroup('?? ??'); //Group formatting, output: \"ab cd\"\n\n//Get the substring\n'foo.bar.abc'.firstItem('.'); //Gets the substring before the first specified string, output: \"foo\"\n'foo.bar.abc'.lastItem('.'); //Gets the substring after the last specified string, output: \"abc\"\n'foo.bar.abc'.beforeItem('.'); //Gets the substring before the last specified string, output: \"foo.bar\"\n'foo.bar.abc'.afterItem('.'); //Gets the substring after the first specified string, output: \"bar.abc\"\n'abc (foo) def'.getPart('(', ')'); //Gets the string between two strings, output: \"foo\"\n\n//Getter properties\n'allen'.capital; //Convert the first char to an uppercase char, output: \"Allen\"\n'<div>'.htmlText; //to HTML text, output: \"&lt;div&gt;\"\n'<div>\\n</span>'.htmlLine; //to single line HTML text\n'2021-03-12 14:19:39.614'.dateValue; //to date value with format \"y-MM-dd HH:mm:ss.SSS\"",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: call-python.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "true",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "// Please deploy Python related jars before using these feature: https://www.geejing.com/site/python/graal.zip\n// eg: copy these jars to \"apache-tomcat/graal\" folder\n// call demo.py to get the text content of wb/system/config.json\n// pyArray = Polyglot.eval('python', '[1,2,42,4]'); // Call Python code directly\nlet result = Polyglot.evalFile('python', 'wb/modules/example/coding/server-js/demo.py');\nWb.send(result);"
  },
  "_icon": "module"
}

File name: common-usage.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//This example demonstrates the general usage of server JavaScript programming and other language programming\n//For more resources, please visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript\n\n//Acess Java classes\n//Access arbitrary Java classes starting with the \"Packages\" keyword. Not recommended.\nlet file = new Packages.java.io.File('/path');\n//Package name starts with \"com\", \"java\", \"org\", etc. can omit the \"Packages\" keyword. Not recommended.\nlet ArrayList = new java.util.ArrayList();\n//Using \"Java.type\" to get classes has higher performance and applicability. Recommended.\nlet HashMap = Java.type('java.util.HashMap');\n//Create a HashMap object, some classes like HashMap/FileUtil/IOUtils are predefined and ready to use.\nlet map = new HashMap();\n\n//Java objects to JS objects. Usually Java objects can be used directly in JS without conversion,\n//and the system will automatically convert to the corresponding type.\nstrArrType = Java.type(\"java.lang.String[]\"); //Defines the String[] type\njavaArr = new strArrType(3); //Create an array of length 3\njsArray = Java.from(javaArr); //Java array to JS array\n\n//Js objects to Java objects. Usually the system automatically converts JS objects when they are passed to Java,\n//and in some cases the conversion can be specified in the following ways:\njsArr1 = [\"a\", \"b\", \"c\"]; //Define the JS array\nstrArrType1 = Java.type(\"java.lang.String[]\"); //Define String[]\njavaArr1 = Java.to(jsArr1, strArrType1); //JS array to Java array\n\n//Java-related functions\njsJavaObj = Java.isJavaObject(123); //Determine whether it is a Java object\nisCls = Java.isType(HashMap); //Determine whether it is a Java Type\ntypeName = Java.typeName(HashMap); //Gets the class name, return: java.util.HashMap\n\n//Call overloaded Java methods\nSystem.out.println(123); //Automatically adapt parameter types\nSystem.out['println(boolean)'](true); //Explicitly specify function with Boolean parameter\nSystem.out['println(char[])']([1, 2]); //Explicitly specify function with char[] parameter\nSystem.out['println(java.lang.String)']('foo'); //Explicitly specify function with String parameter\n\n//Access to Java array/list\nObjectArray = Java.type('java.lang.Object[]');\nobjArray = new ObjectArray(3); //Create an array of length 3\nobjArray[0] = 1;\nobjArray[1] = 'abc';\nobjArray[2] = new JavaDate();\nobjArray.forEach(item => Wb.log(item)); //Iterate through the array\nobjArray.each(item => {\n  Wb.log(item);\n  if (item == 1)\n    return false; //Break iteration\n});\nobjArray.sort(); //call native sort function\nobjArray.mixSort(); //call mixSort function, see WebBuilder docs.\n\n//Access to Java Map\nmap = new HashMap();\nmap.put('abc', 123);\nmap.put('foo', 'bar');\nabc = map.abc; //123\nmap.forEach((k, v) => Wb.log(k + '=' + v)); //Traverse the Map\nmap.each((k, v) => {\n  Wb.log(k + '=' + v);\n  if (k == 'abc')\n    return false; //Break iteration\n});\n\n//Java String\njavaString = new (Java.type('java.lang.String'))(\"Java\");\njavaString.length === 4; //length is a property, and Java string are treatable as JS string\nbytes = javaString.getBytes('utf-8'); //Call the extended getBytes function to get the utf8 bytes of string\n\n//Java date, usually when calling the Java method, the system will automatically convert the JS date to Java date.\nDateUtil.format(new Date(), 'yyyy'); //The system automatically converts JS dates to Java dates\nnew JavaDate(new Date().getTime()); //Explicitly convert JS dates to Java dates, JavaDate is java.util.Date\n\n//Access JS objects from Java\nmap = { foo: 'bar', abc: 123 };\nJavaClass.javaMethod(map); //The map parameter implements the Map interface in Java, map.get(\"foo\") to get \"bar\"\n\n//JS Object/Array to Java org.json.JSONObject/org.json.JSONArray\njsonObject = Wb.toJava(jsObject); //Use api Wb.toJava to convert to JSONObject\njsonArray = Wb.toJava(jsArray); //Use api Wb.toJava to convert to JSONArray\njsonObject = new JSONObject(Wb.encode(jsObject)); //Convert jsObject to a string and then convert it to JSONObject\njsonArray = new JSONArray(Wb.encode(jsArray)); //Convert jsArray to a string and then convert it to JSONArray\njsonObject = new JSONObject({ foo: 'bar', abc: 123, date: new Date() });//Efficient way, js Object to JSONObject. Please note that \"new Date()\" will return \"{}\"(a JSONObject), because it is a Map in Java.\njsonArray = new JSONArray(Java.to(['abc', 123, new Date()]));//Efficient way, js Array to JSONArray. Please note that \"new Date()\" will return \"{}\"(a JSONObject), because it is a Map in Java.\n\n//Java org.json.JSONObject/org.json.JSONArray to JS Object/Array\njsObject = Wb.toJs(jsonObject); //Use api Wb.toJs to convert to Object\njsArray = Wb.toJs(jsonArray); //Use api Wb.toJs to convert to Array\narray = Wb.decode(jsonArray.toString());\nobject = Wb.decode(jsonObject.toString());\narray = Wb.getProxy(jsonArray); //Access JSONArray via proxy, when jsonArray is large, this method is efficient.\nobject = Wb.getProxy(jsonObject); //Access JSONObject via proxy, when jsonObject is large, this method is efficient.\n\n//app is a global visible object in execution-context, created at start and deleted at end\napp.myVar = 'abc'; //Add myVar to the app\nWb.run('my/file1.xwl'); //\"app.myVar\" is visible in file1.xwl\n\n//Regular traversal\narray.forEach(item => Wb.log(item));\n//Interruptible traversal\narray.each(item => {\n  Wb.log(item);\n  if (item == 'foo')\n    return false; //Break iteration\n});\n//Suggest changing to [array.each(item => {item.method()})], avoid unexpected interruptions caused by return false\narray.each(item => item.method());\n//Traverse the object, return false to interrupt the traversal\nWb.each(object, (k, v) => {\n  console.log(k, v);\n});\n//Iterate Java members\nm = Java.type('java.lang.Math')\nfor (i in m) { print(i); }\n\n//Java class extends\nJavaExt = Java.extend(Java.type(\"some.AbstractClass\"), Java.type(\"some.Interface1\"));\nimpl = new JavaExt({\n  superclassMethod: function () { },\n  interfaceMethod: function () { },\n  toString() { return \"MyClass\"; }\n});\nimpl.superclassMethod();\nsw = new (Java.type(\"java.io.StringWriter\"));\nFilterWriterAdapter = Java.extend(Java.type(\"java.io.FilterWriter\"));\nfw = new FilterWriterAdapter(sw, {\n  write: function (s, off, len) {\n  }\n});\nfw_super = Java.super(fw); //call super\nfw.write(\"abc\");\n\n//Java classes variables\nnew HashMap();//HashMap is java.util.HashMap\nnew JavaDate(); //JavaDate is java.util.Date\nIOUtils.copy(source, dst); //IOUtils is org.apache.commons.io.IOUtils\n\n//Run ServerScript in Java\nresult = com.wb.graal.Executor.run(\"let a=123;return Wb.run('my/file.xwl');\"); // Run any script\nresult = com.wb.graal.Executor.execute(\"Wb.sql\", \"select * from wb_user\"); // Run Wb.sql function and get result\nresult = com.wb.graal.Executor.execute(\"Wb.loadCall\", \"wb/ss/myModule.mjs\", \"myExportFunc\", \"param1\", \"param2\"); // Call exposed function in a specified module\n\n//High frequency call JS function in Java code\nfn = new com.wb.graal.JSFunc(\"Wb.encode\");\n// fn = new com.wb.graal.JSFunc(\"Wb.encode\", request, response); // share request and response. This way, parameters can be shared\ntry {\n    for (i = 0; i < 1000; i++)\n      fn.execute(i);\n} finally {\n  fn.close();\n}\n\n//Call other programming languages directly\n//When using other languages, please install the corresponding language first,\n//and the installation method is to run it in the bin directory of the JDK:\n//gu install python //Install python\n//gu install R //Install R\npyArray = Polyglot.eval('python', '[1,2,42,4]'); //Call Python code directly\nrArray = Polyglot.eval('R', 'runif(1000)'); //Call R code directly\nrFunc = Polyglot.evalFile('R', 'myExample.r'); //call R file\nrResult = rFunc(); //Call R function",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: database-access.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Access database by JDBC\nlet conn, statement, resultSet;\n\ntry {\n  conn = DataSource.getConnection('wb-sqlserver');\n  statement = conn.prepareStatement('select user_name from wb_user where sid=?');\n  statement.setString(1, 'admin');\n  resultSet = statement.executeQuery();\n  while (resultSet.next()) {\n    Wb.log(resultSet.getString(1));\n  }\n} finally {\n  //Note that the closing order is resultSet, statement, conn\n  //SysUtil.close performs the close without throwing any exception\n  SysUtil.close(resultSet);\n  SysUtil.close(statement);\n  SysUtil.close(conn);\n}\n\n//Gets the default encapsulated db connection, which is automatically closed after the execution-context is completed\n//regardless of whether an exception occurs\nconn = Wb.getConn(); //Gets a shared connection of the default database\nconn = Wb.getConn('wb-sqlserver'); //Gets the shared connection with the specified name\nconn = new Wb.Connection('wb-sqlserver'); //Create a new stand-alone connection\nconn.startTrans(); //Start transaction\nconn.startTrans('serializable'); //Start transaction with the specific isolation level\nconn.commit(); //Commit transaction\nconn.rollback(); //Rollback transaction\n\n//Run arbitrary SQL by Wb.Query and immediately release all resources except the db shared connection,\n//which is automatically closed after the execution-context is completed. See Wb.Query.run docs.\n//Result is an object composed of all results returned by SQL, including any number of return values and output parameters\nresult = Wb.sql('select * from wb_user where sid={?param1?}');\ncount = Wb.sql('delete from table1 where 1 = 0', 'wb-oracle'); //\"wb-oracle\" is the database name\nWb.sql({ sql: 'select * from table1', db: 'wb-oracle', fn(row) { process(row); } });\n//Access stored procedure, {?inParam?} is the input parameter and {*outParam*} is the output parameter\nWb.sql('{call sp({?timestamp|inParam?}, {*cursor|p1*}, {*double|3|p2*})}');\n//Perform a batch operation to insert records\nWb.sql({\n  sql: 'insert into test (sid, varchar_field) values({?sid?}, {?varchar_field?})',\n  params: [{ sid: 'foo', varchar_field: 'bar' }, { sid: 'abc', varchar_field: 'def' }]\n});\n//Set fn to traverse all records, and specify fn to traverse all records by default\nWb.sql({\n  sql: 'select * from wb_perm', fn(item, name, index) {\n    if (index > 10)\n      return false; //Break traversal\n    Wb.log(item)\n  }\n}); \n\n//Gets the first result set returned by SQL\nrows = Wb.getRows('select * from wb_user'); //Gets rows in object format\nWb.table(rows); //Display the data as a table in the IDE devtools console\nrows = Wb.getAllRows('select * from wb_user'); //Gets all rows in object format, recommend using fn parameter\nrecs = Wb.getRecords('select * from wb_user'); //Gets records in array format\nWb.table(recs);\nrecs = Wb.getAllRecords('select * from wb_user'); //Gets all records in array format, recommend using fn parameter\nWb.getRows('select * from wb_user', 'wb-oracle'); //Access the specific database\nWb.getRecords({ sql: 'select * from wb_user', db: 'wb-oracle', blob: 'text' }); //Use object as parameter configurations\n\nrow = Wb.getRow('select * from wb_user'); //Gets the first row int object format\nrec = Wb.getRecord('select * from wb_user'); //Gets the first record int array format\n\n//Gets object that includes columns metadata, rows and others, rows in object format\nrows = Wb.getRowx('select * from wb_user');\n//Gets object that includes columns metadata, records and others, records in array format\nrecs = Wb.getRecordx('select * from wb_user');\nrows = Wb.getDict('select * from wb_user'); //Gets the dictionaryized object by Wb.getRowx\nrecs = Wb.getDictRecords('select * from wb_user'); //Gets the dictionaryized object by Wb.getRecordx\n\nrs = Wb.sqlRS('select * from wb_user').resultSet; //Run SQL to get the ResultSet\nrows = Wb.sqlAll('select * from test'); //Gets rows for all values including blob values\nrows = Wb.Query.getRows(resultSet, configs); //Gets rows in object format by ResultSet\nrecords = Wb.Query.getRecords(resultSet, configs); //Gets records in array format by ResultSet\n\nWb.sendRows('select * from wb_user'); //Send the results generated by Wb.getRows\nWb.sendRow('select * from wb_user'); //Send the results generated by Wb.getRow\nWb.sendRecords('select * from wb_user'); //Send the results generated by Wb.getRecords\nWb.sendRecord('select * from wb_user'); //Send the results generated by Wb.getRecord\nWb.sendRowx('select * from wb_user'); //Send the results generated by Wb.getRowx\nWb.sendRecordx('select * from wb_user'); //Send the results generated by Wb.getRecordx\nWb.sendDict('select * from wb_user'); //Send the results generated by Wb.getDict\nWb.sendDictRecords('select * from wb_user'); //Send the results generated by Wb.getDictRecords\n\n//Use JS string template to define large SQL\nsql = `\n  select user_name as Name from wb_user\n  where sid='admin'\n`;\n\n//Access stored procedures\n\n//Create the following stored procedure in SQL Server:\nWb.sql(`\n  create procedure test_proc\n  (\n  @inParam int, -- in param\n  @outParam int out -- in and out param\n  )\n  as\n  begin\n    set @outParam=@inParam+2 --Returns the output parameters\n    select * from wb_user --Returns ResultSet 1\n    delete from wb_key where 1=0 --Returns the affected rows\n    select * from wb_role --Returns ResultSet 2\n    return 123 --returned value\n  end\n`, 'wb-sqlserver');\n\n//Call the above test_proc procedure and get a total of 5 results:\n//including outParam, returned value, 2 ResultSets, and 1 affected row\n//Run the stored procedure using Wb.sql, {*type|name*} represents output parameter, {?type|name?} represents input parameter\n//You can also run the following SQL in [Management Tools]->[Database Explorer]:\n// {{*returnValue*}=call test_proc(3,{*outParam*})}\n//to get the return 5 results\nWb.set({ myInParam: 3 }); //Set the input parameter value\nlet result = Wb.sql('{{*returnValue*}=call test_proc({?myInParam?},{*outParam*})}', 'wb-sqlserver'); //Run sql\nWb.log(result); //Display the results in the IDE devtools console\nWb.log(result.$return[0]); //Get the first ResultSet\nWb.log(result.outParam); //Get outParam\n\n//Create the following stored procedure in Oracle:\nWb.sql(`\n  CREATE OR REPLACE NONEDITIONABLE PROCEDURE USER_PROC\n  (\n  P_USER OUT TYPES.X_CURSOR,\n  P_NAME IN VARCHAR\n  )\n  AS\n  BEGIN\n  OPEN P_USER FOR SELECT * FROM WB_USER WHERE USER_NAME = P_NAME;\n  END USER_PROC;\n`, 'wb-oracle');\n//Send the first ResultSet output parameter p_user to the client\n//The \"cursor\" in {*cursor|p_user*} explicitly indicates that the output type of p_user is cursor\nWb.sendRows(`{call user_proc({*cursor|p_user*}, 'admin')}`, 'wb-oracle');\n\n//select, insert, delete, update, and synchronize\n//Insert, delete, or update the specified data to my_table in a transaction.\n//Field names starts with \"$\" are represented as old values, and field names starts without \"$\" represent new values.\nWb.sync({\n  tableName: 'my_table',\n  insert: [{ field1: 'ab', field2: 12 }, { field1: 'cd', field2: 34 }],\n  update: [{ field1: 'newValue', $field1: 'oldValue' }],\n  del: [{ $field1: 'xyz' }]\n});\n//Specify whereFields\nWb.sync({\n  tableName: 'my_table',\n  update: [{ field1: 'newValue', $field1: 'oldValue', $field2: 'abc' }],\n  //Explicitly specify field1 and field2 as the where key fields, otherwise the system will automatically get the key fields\n  whereFields: 'field1, field2'\n});\n//Perform a conditional select\nWb.sync({\n  tableName: 'wb_user',\n  fields: 'sid, user_name',\n  select: { $sid: 'admin' }\n});\n//Downloads the specified BLOB field as a file\nWb.sync({\n  tableName: 'wb_resource',\n  download: { _meta: { fieldName: 'svalue', filename: 'desktop.json' }, $sid: 'admin@desktop' }\n});",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: devtools-debug.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "html": "<div cid='lang-en' class='w-html' style='line-height:2;display:none;'>\n  <div>This example demonstrates how to remotely debug server-side code using the browser \"devtools\":</div>\n  <ol>\n    <li>Add the module file \"test.xwl\" and write arbitrary code in the serverScript property, such as \"Wb.log('test')\".\n    </li>\n    <li>Select the \"test.xwl\" module and click IDE menu [Run] ->[Toggle Debugging Status].</li>\n    <li>Click the IDE menu [Run] -> [Run] to run \"test.xwl\".</li>\n    <li>Click the \"Debugging\" tab card at the bottom of the IDE, and double-click the \"test1.xwl\" debugging item to open\n      new window.</li>\n    <li>Click the address bar in the new window, press \"Ctrl+V\" to paste the debugging address, and press Enter to open\n      the \"devtools\" debugging page.</li>\n    <li>After adding the keyword \"debugger\" anywhere in the script, the code will pause and wait for debugging there.\n    </li>\n    <li>The debugging URL is the same in the same session and the same module, debugging multiple times only requires\n      refreshing the \"devtools\" debugging page after running the module.</li>\n    <li>When debugging remotely, due to security reasons, the browser may block access to the debugging page and prompt\n      that the page was not found. In this case, you can press [F12] key to open \"devtools\" and then press Enter key in\n      the browser address bar.</li>\n  </ol>\n</div>\n<div cid='lang-zh-cn' class='w-html' style='line-height:2;display:none;'>\n  <div>该示例演示了如何使用浏览器\"devtools\"远程调试服务器端代码:</div>\n  <ol>\n    <li>添加模块文件\"test.xwl\",并在serverScript属性中编写任意代码,如:\"Wb.log('test')\"。</li>\n    <li>选择\"test.xwl\"模块,并点击IDE菜单[运行]->[切换调试状态]。</li>\n    <li>点击IDE菜单[运行]->[运行],运行\"test.xwl\"。</li>\n    <li>点击IDE底部的\"调试\"选项卡,并双击\"test1.xwl\"调试项打开新窗口。</li>\n    <li>在新窗口中点击地址栏,按\"Ctrl+V\"粘贴调试地址,并按回车键打开\"devtools\"调试页面。</li>\n    <li>在脚本任意处添加关键字\"debugger\"后,代码将在该处暂停运行并等待调试。</li>\n    <li>同一个模块文件在同一个HTTP会话内调试的URL地址不变,多次调试只需运行模块后刷新\"devtools\"调试页面即可。</li>\n    <li>当远程调试时,基于安全原因,浏览器可能阻止对调试页面的访问并提示页面未找到,此时可以按[F12]键打开\"devtools\"并在浏览器地址栏按回车键即可。</li>\n  </ol>\n</div>",
        "autoScroll": "true"
      },
      "events": {
        "ready": "let div, el = this.el;\n\nif (Str.lang == 'zh-cn')\n  div = el.query('[cid=lang-zh-cn]');\nelse\n  div = el.query('[cid=lang-en]');\ndiv.setStyle('display', 'block');"
      }
    }
  ]
}

File name: file-access.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Use java.io.File to acess files\nlet file = new File(Base.path, 'wb/js/wb.js'); //File = java.io.File\nlet text = FileUtils.readFileToString(file, Wb.utf8); //Use org.apache.commons.io.FileUtils.readFileToString\nWb.send(text); //Send text to the client\n\n//Use Wb.File to access files\nfile = new Wb.File(true, 'wb/js/wb.js'); //Create file object\ntext = file.text; //Re-read the file text contents\nWb.send(text); //Send text to the client\nfileHandle = file.file; //file's file property is java.io.File\n\n//Read JSON objects or arrays in a file\nWb.log(new Wb.File(true, 'wb/system/config.json').object);\n\n//Read and write files\nfile = new Wb.File(true, 'test.txt');\nfile.text = 'abc'; //Save \"abc\" to a file immediately\nfile.object = { foo: 'bar', abc: [1, 2, 3] }; //Save the JSON object or array to a file immediately\nWb.log(file.object); //Re-read the JSON object in the file\nfile.bytes = [1, 2, 3]; //Save bytes to a file immediately\nWb.log(Wb.encode(file.bytes)); //Re-read the byte array in the file\nbs = file.byteStream; //Re-read the ByteArrayInputStream in the file\nfile.stream = inputStream; //Save the inputStream to a file immediately\ninputStream = file.stream; //Re-read the buffered FileInputStream in the file\nbase64 = file.base64; ///Re-read the content represented by base64 in the file\nfile.base64 = base64; //Save the content represented by base64 to a file immediately\n\n//File name and path access\nname = file.name; //Get file name\nfile.name = name; //Set file name\nextension = file.extension; //Get file name extension\nfile.extension = extension; //Set file name extension\nnormalName = file.normalName; //Get a file name without an extension\npath = file.path; //Get file path\nrelPath = file.relPath; //Get file relative path under the application directory\n\n//Get file iist\nWb.File.listRoots(); //List root files\nfiles = file.listFiles(sortType, desc); //List files of folder\n\n//Traverse\nfile.each(f => Wb.log(f.name)); //Traverse the current directory\nfile.cascade(f => Wb.log(f)); //Iterate through all subfiles and subdirectories\nfile.cascadeSelf(f => { //Iterate include file itself \n  Wb.log(f);\n  if (f.name == 'myfile1.txt')\n    return false; //Break traversal\n  if (f.name == 'myfile2.txt')\n    return null; //The remaining files in the current directory will no longer be traversed\n});\nfile.bubble(f => Wb.log(f)); //Traverse the current file/directory and all parent directories\nfile.bubbleParent(f => Wb.log(f)); //Traverse without current file/directory\n\n//File lock, only synchronous multithreaded access, and does not lock the file itself\nfile.lock(); //Thread lock\ndoSomething();\nfile.unlock(); //Unlock the thread immediately. The block will auto unlock when execution-context finished.\n\n//Others\nfile.createFile(); //Create a file\nfile.createFolder(); //Create a file\nfile.remove(); //Remove a file or folder\nexists = file.exists; //Determine whether the file exists\nlastModified = file.lastModified; //Last modified date as a numeric value\nlastModifiedDate = file.lastModifiedDate; //Last modified date\nisEqual = file1.equals(file1); //Whether the paths are the same\nisFile = file.isFile; //Determine whether it is a file\nisFolder = file.isFolder; //Determine whether it is a folder\nlength = file.length; //File size\nparent = file.parent; //File parent directory",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: load-modules.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Loads a JS/MJS script file at the specified path. Use Wb.load method to load only once,\n//and if it is already loaded, the script will no longer be executed.\nWb.load('wb/ss/my.js'); //Load files based on the root directory of the web-app\nWb.load('./foo/bar.mjs'); //Load the foo/bar.mjs file in the current directory\nlet module = Wb.load('../foo/my.mjs'); //Load the foo/my.mjs module file in the parent directory and get exported object\nmodule.exportObject.method(); //Call the method of exportObject\n\n//Run ServerScript of the xwl module, and the ServerScript will be executed every time\nWb.run('shortcut'); //Run modules through module shortcut URL\nWb.run('my/file.xwl', {foo:'bar'}); //Run the my/file module in the module root directory and pass the parameters\nWb.run('./my/file.xwl'); //Run the my/file module in the current directory\nWb.run('../my/file'); //Run the my/file module in the parent directory (.xwl extension can be omitted)\nWb.run('m?xwl=my/file'); //Run the module through URI\nWb.log(Wb.run('myModule')); //Log the return value in myModule's ServerScript\n\n//Call the xwl module, Wb.execute/Wb.invoke differs from Wb.run in that it retrieves client script\n//Invoke the module, run the called module's ServerScript and output client script\nWb.execute('my/file');\nWb.invoke('my/file');\n//Invoke the module, run the called module's ServerScript and get the client full HTML to script variable\nlet script = Wb.invoke('my/file', params, true, false);",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: logs.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Output to the IDE devtools console associated with the specified user.\n//These methods are recommended to show logging information\nWb.log('log'); //Output log message to IDE console of current user, if current user is not available then output to the anonymous user(Config item: \"sys.ss.anonymousDebugUser\")\nWb.log('admin-log', \"admin\"); //Output log message to IDE console of admin user\nWb.log('all-log', true); //Output log message to IDE console of all users\nWb.info('info');\nWb.info('all-info', true);\nWb.warn('warn');\nWb.warn('all-warn', true);\nWb.error('error');\nWb.error('all-error', true);\nWb.table([{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }]); //Output table to IDE console of the current user\nWb.table([{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }], true); //Output table to IDE console of all users\nWb.debug('debug');\nWb.debug('all-debug', true); //Output \"debug\" to IDE console of all users\n\n//Output to the IDE devtools console and the server file logs.\nWb.recordTrace('trace');\nWb.recordDebug('debug');\nWb.recordInfo('info');\nWb.recordWarn('warn');\nWb.recordError('error');\nWb.recordExcept(new Classes.NullPointerException('exception'));\nWb.recordFatal('fatal');\n\n//Output to the IDE devtools console and the server db logs.\nWb.recordInfox('info', 'myInfo');\nWb.recordWarnx('warn');\nWb.recordErrorx('error');\nWb.recordExceptx(new Classes.NullPointerException('exception'));\n\n//Output to the server logs\nLogUtil.trace('trace');\nLogUtil.trace('trace', request); //Logs object as trace and outputs to the client console associated with the specified session or request.\nLogUtil.debug('debug');\nLogUtil.info('info');\nLogUtil.warn('warn');\nLogUtil.error('error');\nLogUtil.except(new Classes.NullPointerException('exception'));\nLogUtil.fatal('fatal');\n\n//Output to the server db\nLogxUtil.info('info');\nLogxUtil.info('info', 'type');\nLogxUtil.info('info', 'type', request); //Logs object as information and outputs to the client console associated with the specified session.\nLogxUtil.warn('warn');\nLogxUtil.error('error');\nLogxUtil.except(new Classes.NullPointerException('exception'));\n\n//Output to the remote debugging devtools console\nconsole.log('log');\nconsole.info('info');\nconsole.debug('debug');\nconsole.warn('info');\nconsole.error('error')\nconsole.assert(1 == 2, 'not equal');\nconsole.clear();\nconsole.count();\nconsole.countReset();\nconsole.group();\nconsole.groupEnd();\nconsole.time();\nconsole.timeLog();\nconsole.timeEnd();\n\n//Others\nSystem.out.println('msg'); //Output to the system console\nprint('msg'); //Output to the system console and the remote debugging devtools console",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: multi-threads.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Multithreaded programming\n\n//Create a new thread and get the result returned by the thread\nlet map = Wb.startThread(params => {\n  Wb.log(params.date);\n  Wb.sleep(100);\n  return { result: 'ok' };\n}, { foo: 'bar', abc: 123, date: new Date() });\nmap.thread.join(); //Wait for the thread\nWb.log(map.result); //Displays the results: {result: 'ok'}\n\n//Each thread has an exclusive execution-context, and the JS objects can only be accessed by the\n//current execution-context, so no concurrency control is required.\n//Concurrency control still needs to be handled when accessing Java objects, such as when accessing\n//a globally shared Java static object.\nBase.map.put('foo', 'bar'); //Add variables to the global static Java ConcurrentHashMap object\n//Add \"myVar\" to the global JS object app in the execution-context, which is visible only to the current execution-context\napp.myVar = 'value';\nWb.run('my/file1'); //Call the file1 module to safely access app.myVar\n\n//Sync lock\n//Create a lock named my.lock1, and when executed again, the code will be blocked until the lock is released\nlet lock = new Wb.Lock('my.lock1');\ntry {\n  let myJavaObject = BaseMap.get('myJavaObject'); //Access BaseMap\n  myJavaObject.doSomeThing(); //Execution is safe in locking\n} finally {\n  lock.unlock(); //Release the lock. The block will auto unlock when execution-context finished.\n}",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: session.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Gets the current user session\nlet session = request.getSession();\n//Gets the current user name\nlet username = Wb.username;\n//Gets the current user ID\nlet userid = Wb.userid;\n//Gets the current user display name\nlet dispname = Wb.dispname;\n//Gets the current user role\nlet roles = Wb.roles;\n\n//Determine whether the current user can access dbe.xwl\nlet result = Wb.permit('admin/dbe.xwl');\n//Determine whether the current user contains a role\nlet hasRole = Wb.hasRole('admin');\n//Determine whether the current user contains some roles\nhasRole = Wb.hasRole(['admin', 'manager']);\n//If the specified role is not included, the specified exception information is thrown\nWb.permitRole('manager', 'Only manager is allowed');\nWb.permitRole(['manager', 'test'], 'Only manager and test are allowed');\n//If the specified role is included, the specified exception information is thrown\nWb.forbidRole('manager', 'manager is not allowed');\nWb.forbidRole(['manager', 'test'], 'manager and test are not allowed');\n\n//Gets the session list map of the specified user\nlet map = Sessions.getSessions('admin');\n//Get all the corresponding websocket sessions by user id\nlet list = Sessions.getWSSessions('admin', 'sys.ide');",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: web-access.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "edit",
      "text": "codeEditor1",
      "cls": "Wb.CodeEditor",
      "_expanded": true,
      "properties": {
        "cid": "codeEditor1",
        "full": "true",
        "value": "//Gets the parameters submitted by the client. It is commonly used in application/x-www-form-urlencoded,\n//multipart/form-data and application/json\nlet val = Wb.get('paramName'); //Gets session attribute, request attribute and request parameters\nval = Wb.getString('paramName'); //Gets the parameter and converts it to string\nval = Wb.getBool('paramName'); //Gets the parameter and converts it to boolean\nval = Wb.getInt('paramName'); //Gets the parameter and converts it to int\nval = Wb.getFloat('paramName'); //Gets the parameter and converts it to float\nval = Wb.getDate('paramName'); //Gets the parameter and converts it to date\nval = Wb.getObject('paramName'); //Gets the parameter and converts it to Object/Array\n//Gets multiple parameters with the same name, always returning array or Java List. Example: Get multiple uploaded files\nval = Wb.getParams('paramName');\nval = Params.paramName; //Get the value through the proxy, same as Wb.get\nval = Params[name];\nlet hasParam = Wb.has('paramName'); //Determine whether the parameter exists\nlet payload = Wb.payload; //Gets the payload content of the request\nlet payloadParams = Wb.payloadParams; //Gets the application/json payload object\n\n//Set attribute values\nWb.set('paramName', 'abc'); //set \"abc\" to request attribute \"paramName\", same as request.setAttribute('paramName', 'abc');\nParams.paramName = 'abc'; //Set attribute by Proxy, same as above\nParams[name] = 'abc'; //Same as above\n\n//Send object to the client\nWb.send('text'); //Send text\nWb.send(123); //Send after converting the number to text\nWb.send(new Date()); //Send after converting the date to text\nWb.send({ foo: 'bar' }); //Send after encoding the object to text\nWb.send([1, 2, 3]); //Send after encoding the array to text\nWb.send(inputStream); //Send inputstream\nWb.send(bytes); //Send byte array\nWb.send('text', 'mySocket'); //Send text to the current user webSocket with the name \"mySocket\"\nWb.send(inputStream, 'mySocket', 'admin'); ///Send inputStream to all \"admin\" users webSocket with the name \"mySocket\"\nWb.send({ type: 'log', style: 3, data: 'msg' }, 'sys.ide', true); //Send content to the webSocket of the IDE\n\n//Gets the uploaded file\nlet file = Wb.getParams('fileInput'); //Gets single file\nfilename = file.name; //Gets filename\nstream = file.stream; //Gets file inputstream\nsize = file.size; //Gets file size\nlet files = Wb.getParams('fileInput'); //Gets single or multiple files\nfiles.forEach(file => {\n  new Wb.File(true, 'upload/' + filename).stream = file.stream; //Save the uploaded file to the specified directory\n  //Set the stream to a parameter named \"myStream\". Note that the inputstream is unidirectional,\n  //and if the stream is already in use, the stream pointer will point to the end of the stream.\n  Params.myStream = file.stream;\n  Wb.sql('insert into table values({?blob|myStream?})'); //Insert the uploaded file into the database table blob field\n});\n\n//Send HTTP requests\nlet html = Wb.submit('https://developer.mozilla.org'); //Sends a GET request to the specified URL and gets the returned content\n//Sends with parameters\nWb.submit({ url: 'http://localhost:8080/wb/m?xwl=test2', params: { foo: 'bar' } });\n//Sends with payload data\nWb.submit({ url: 'http://localhost:8080/wb/m?xwl=test2', data: payloadObject });\n//Login to the system, verify the username and password, and get the cookie token\nlet cookie = Wb.submit({\n  url: 'http://localhost:8080/wb/verify', all: true, params: { username: 'admin', password: 'admin' }\n}).cookie;\n//Access module1 with login cookie token\nlet result = Wb.submit({ url: 'http://localhost:8080/wb/m?xwl=module1', cookie }, { foo: 'bar' });\n//Logout\nWb.submit({ url: 'http://localhost:8080/wb/logout', cookie });",
        "wrapBorder": "0",
        "readonly": "true"
      }
    }
  ]
}

File name: demo-source.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  // staff dict data\n  staffDict() {\n    Wb.sendDict('select code, full_name from wb_staff' + Wb.getOrderSql(), 'wb,');\n  },\n  // staff dict data desc\n  staffDictDesc() {\n    Wb.sendDict('select code, full_name from wb_staff order by code desc', 'wb,');\n  },\n  // staff dict all data\n  staffAllDict() {\n    if (!this.downloadBlob())\n      Wb.sendDict('select * from wb_staff' + Wb.getOrderSql(), 'wb,');\n  },\n  // staff rows\n  staffRows() {\n    Wb.sendRows('select code, full_name from wb_staff');\n  },\n  // staff row\n  staffFirstRow() {\n    Wb.sendRow(\"select * from wb_staff where code='10001'\")\n  },\n  // search staff rows\n  staffSearch() {\n    let sql;\n\n    sql = 'select code, full_name, email from wb_staff';\n    if (Params.query) {\n      Wb.setLike('query');\n      sql += ' where full_name like {?query?}';\n    }\n    sql += ' order by code'\n    Wb.sendRows(sql);\n  },\n  // list continents\n  listRootArea() {\n    Wb.sendDict(`select * from wb_area where parent_id='0' order by area_name`);\n  },\n  // list areas\n  listArea() {\n    Wb.sendDict('select * from wb_area where parent_id={?parent_id?} order by area_name');\n  },\n  // list areas tree\n  listAreaTree() {\n    Params.sid ??= '0'; //\"0\" is root parent_id\n    Wb.sendDict(`\n    select area_name as text, sid as subtext, 0 as \"_checked\", area_name, sid, parent_id,\n      case when (select count(*) from wb_area b where b.parent_id=a.sid)>0 then 0 else 1 end as \"_leaf\"\n      from wb_area a where parent_id={?sid?} order by area_name\n    `);\n  },\n  // list areas tree without check box\n  listAreaTree1() {\n    let whereClause = '';\n\n    Params.sid ??= '0'; //\"0\" is root parent_id\n    if (Params.query) {\n      Wb.setLike('query');\n      whereClause = 'area_name like {?query?}';\n    } else {\n      whereClause = 'parent_id={?sid?}'\n    }\n    Wb.sendDict(`\n    select area_name as text, sid as subtext, area_name, sid, parent_id,\n      case when (select count(*) from wb_area b where b.parent_id=a.sid)>0 then 0 else 1 end as \"_leaf\"\n      from wb_area a where ${whereClause} order by area_name\n    `);\n  },\n  // search areas rows\n  areaSearch() {\n    let sql;\n\n    sql = 'select * from wb_area';\n    if (Params.query) {\n      Wb.setLike('query');\n      sql += ' where sid like {?query?}';\n    }\n    Wb.sendRows(sql);\n  },\n  // Database side pagination\n  staffDbPaging() {\n    if (this.downloadBlob())\n      return;\n    let db = Wb.getConn();\n\n    if (db.match('derby')) {\n      //For derby only, some paging parameter: _from, _to, _size\n      Wb.sendDict({\n        sql: 'select * from wb_staff offset {?bigint|_from?} rows fetch first {?bigint|_size?} rows only',\n        db, dict: 'wb', paging: false /* stop app serverside paging */, total: 'select count(*) from wb_staff'\n      });\n    } else {\n      //For other database pagination SQL, please refer to their respective manuals\n      //Other databases here do not use database pagination\n      Wb.sendDict({ sql: 'select * from wb_staff', db, dict: 'wb' })\n    }\n  },\n  // Perform download if necessary.\n  downloadBlob() {\n    let download = Wb.getObject('download');\n    if (download) {\n      //download blob\n      let row = Wb.getRow({\n        sql: 'select full_name, photo from wb_staff where sid={?$sid?}',\n        blob: true,\n        params: download\n      });\n      if (row?.photo)\n        Wb.exportData(row.photo, row.full_name + '.png');\n      else\n        Wb.raise('Photo not found.');\n      return true;\n    } else\n      return false;\n  },\n  // Check username\n  checkUser() {\n    Wb.send(Wb.getRow('select 1 from wb_user where user_name={?username?}') ? 'failed' : 'ok');\n  },\n  // Select admin from staff\n  selectAdminStaff() {\n    let sql;\n\n    sql = \"select sid,full_name as text,code as subtext from wb_staff where user_id='admin'\";\n    if (Params.search) {\n      Wb.setLike('search');\n      sql += ' and full_name like {?search?}';\n    }\n    Wb.sendRowx(sql);\n  },\n  // Select none admin from staff\n  selectNoneAdminStaff() {\n    let sql;\n\n    sql = \"select sid,full_name as text,code as subtext from wb_staff where user_id<>'admin'\";\n    if (Params.search) {\n      Wb.setLike('search');\n      sql += ' and full_name like {?search?}';\n    }\n    Wb.sendRowx(sql);\n  },\n  // Select report1\n  selectReport1() {\n    Wb.sendRowx('select * from wb_report_demo1')\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: button.xwl


{
  "title": "",
  "icon": "button",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "finalize": "app.useIdeTipField.highlight();"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Button",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "label1",
          "text": "useIdeTipField",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "useIdeTipField",
            "icon": "info",
            "htmlValue": "true",
            "value": "For a better learning experience, please review the examples in the <a href=\"ide?openExample=1\"   target=\"_blank\">WebBuilder IDE</a>"
          }
        },
        {
          "_icon": "title",
          "text": "listTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "listTitle",
            "title": "Button list"
          }
        },
        {
          "_icon": "container",
          "text": "listCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "listCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "commonBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "commonBtn",
                "text": "Common Button"
              },
              "events": {
                "click": "Wb.tip(this.cid + ' clicked'); // equals to: Wb.tip(app.commonBtn.cid + ' clicked');"
              }
            },
            {
              "_icon": "button",
              "text": "iconBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "iconBtn",
                "text": "Icon Button",
                "icon": "gear"
              }
            },
            {
              "_icon": "button",
              "text": "imgBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "imgBtn",
                "text": "Image Button",
                "img": "cut"
              }
            },
            {
              "_icon": "button",
              "text": "charIconBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "charIconBtn",
                "text": "Char Icon",
                "icon": "🌻",
                "tip": "Any char as icon"
              }
            },
            {
              "_icon": "button",
              "text": "badgeBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "badgeBtn",
                "text": "58",
                "icon": "bell",
                "badgeText": "true",
                "tip": "Badge text"
              }
            },
            {
              "_icon": "button",
              "text": "leftBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "leftBtn",
                "text": "Left",
                "icon": "left4",
                "iconAlign": "left"
              }
            },
            {
              "_icon": "button",
              "text": "rightBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "rightBtn",
                "text": "Right",
                "icon": "right4",
                "iconAlign": "right"
              }
            },
            {
              "_icon": "button",
              "text": "topBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "topBtn",
                "text": "Top",
                "icon": "up4",
                "iconAlign": "top"
              }
            },
            {
              "_icon": "button",
              "text": "bottomBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "bottomBtn",
                "text": "Bottom",
                "icon": "down4",
                "iconAlign": "bottom"
              }
            },
            {
              "_icon": "button",
              "text": "keyBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "keyBtn",
                "icon": "keyboard",
                "text": "Ctrl+Shift+K",
                "keys": "Ctrl+Shift+K",
                "tip": "Press Ctrl+Shift+K to fire click event",
                "keysTextTip": "false"
              },
              "events": {
                "click": "Wb.tip(this.cid + ' clicked');"
              }
            },
            {
              "_icon": "button",
              "text": "noFocusBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "noFocusBtn",
                "text": "No Focus",
                "icon": "flower",
                "tabIndex": "undefined",
                "focusable": "false"
              }
            },
            {
              "_icon": "button",
              "text": "menuBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "menuBtn",
                "text": "menu",
                "icon": "menu"
              },
              "items": [
                {
                  "_icon": "menu2",
                  "text": "menu",
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "_expanded": true,
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "item1",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item1",
                        "text": "item 1",
                        "icon": "calendar"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item2",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item2",
                        "text": "item 2"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item3",
                      "cls": "Wb.Item",
                      "_expanded": false,
                      "properties": {
                        "cid": "item3",
                        "text": "item 3"
                      },
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "split",
              "text": "splitButton1",
              "cls": "Wb.SplitButton",
              "properties": {
                "cid": "splitButton1",
                "text": "Split Button"
              },
              "events": {
                "click": "Wb.tip(this.cid + ' clicked');"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "menu2",
                  "text": "menu",
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "_expanded": true,
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "item1",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item1",
                        "text": "item 1",
                        "icon": "calendar"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item2",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item2",
                        "text": "item 2"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item3",
                      "cls": "Wb.Item",
                      "_expanded": false,
                      "properties": {
                        "cid": "item3",
                        "text": "item 3"
                      },
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "button",
              "text": "tapBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "tapBtn",
                "tapClick": "true",
                "text": "MouseDown Click"
              },
              "events": {
                "click": "Wb.tip(this.cid + ' clicked');"
              }
            },
            {
              "_icon": "button",
              "text": "repeatBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "repeatBtn",
                "text": "Repeat Click",
                "repeatInterval": "true"
              },
              "events": {
                "click": "app.repeatCt ??= 0;\nWb.toast(this.cid + ' clicked ' + (app.repeatCt++));"
              }
            },
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "icon": "delete",
                "text": "Disabled",
                "disabled": "true"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "styleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "styleTitle",
            "title": "Button style"
          }
        },
        {
          "_icon": "label",
          "text": "styleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "styleLabel",
            "text": "Set the \"type\" property to set the button style"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "styleCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "styleCt",
            "layout": "form",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "button",
              "text": "primary",
              "cls": "Wb.Button",
              "properties": {
                "cid": "primary",
                "type": "primary",
                "text": "Primary",
                "icon": "address-book"
              }
            },
            {
              "_icon": "button",
              "text": "plain",
              "cls": "Wb.Button",
              "properties": {
                "cid": "plain",
                "type": "plain",
                "icon": "delete",
                "tip": "Plain Button"
              }
            },
            {
              "_icon": "button",
              "text": "tool1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "tool1",
                "type": "tool",
                "text": "cut",
                "icon": "cut",
                "tip": "Text Tool Button"
              }
            },
            {
              "_icon": "button",
              "text": "noFocusTool1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "noFocusTool1",
                "text": "cut",
                "icon": "cut",
                "tip": "Text Tool Button no focus",
                "cname": "tool"
              }
            },
            {
              "_icon": "button",
              "text": "tool2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "tool2",
                "type": "tool",
                "icon": "add1",
                "tip": "Tool Button"
              }
            },
            {
              "_icon": "button",
              "text": "noFocusTool2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "noFocusTool2",
                "icon": "add1",
                "tip": "Tool Button no focus",
                "cname": "tool"
              }
            },
            {
              "_icon": "button",
              "text": "icon",
              "cls": "Wb.Button",
              "properties": {
                "cid": "icon",
                "icon": "database",
                "tip": "Icon Button no focus",
                "cname": "iconButton"
              },
              "_expanded": true
            },
            {
              "_icon": "button",
              "text": "light",
              "cls": "Wb.Button",
              "properties": {
                "cid": "light",
                "icon": "cube",
                "tip": "Light Button no focus",
                "cname": "lightButton"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "shapeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "shapeTitle",
            "title": "Button shape"
          }
        },
        {
          "_icon": "label",
          "text": "shapeLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "shapeLabel",
            "text": "Set the \"shape\" property to set the button shape"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "shapeCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "shapeCt",
            "layout": "form",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "button",
              "text": "circleBtn",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "circleBtn",
                "icon": "gear",
                "shape": "circle",
                "tip": "Circle"
              }
            },
            {
              "_icon": "button",
              "text": "rectBtn",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "rectBtn",
                "icon": "gear",
                "shape": "rect",
                "text": "Rect"
              }
            },
            {
              "_icon": "button",
              "text": "roundBtn",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "roundBtn",
                "icon": "gear",
                "shape": "round",
                "text": "Round"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "groupTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "groupTitle",
            "title": "Grouping and toggle button"
          }
        },
        {
          "_icon": "label",
          "text": "groupLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "groupLabel",
            "text": "Set the \"groupName\" property for grouping and set the \"active\" property for toggle"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "groupCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "groupCt",
            "layout": "form",
            "frame": "true"
          },
          "events": {
            "buttontoggle": "Wb.tip(button.cid + ' is ' + active);"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button 1",
                "groupName": "group1",
                "active": "false"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "groupName": "group1",
                "text": "Button 2",
                "active": "false"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button3",
                "groupName": "group1",
                "text": "Button 3",
                "active": "false"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              },
              "_expanded": true,
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button4",
                "groupName": "group2",
                "text": "Button 4",
                "active": "false"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button5",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button5",
                "groupName": "group2",
                "text": "Button 5",
                "active": "true"
              }
            },
            {
              "_icon": "button",
              "text": "button6",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "button6",
                "groupName": "group2",
                "text": "Button 6",
                "active": "false"
              }
            },
            {
              "_icon": "space",
              "text": "space1",
              "cls": "Wb.Space",
              "properties": {
                "cid": "space1"
              }
            },
            {
              "_icon": "button",
              "text": "button7",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button7",
                "text": "Toggle",
                "active": "false"
              }
            },
            {
              "_icon": "space",
              "text": "space2",
              "cls": "Wb.Space",
              "properties": {
                "cid": "space2"
              }
            },
            {
              "_icon": "button",
              "text": "button8",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button8",
                "text": "No Background",
                "active": "true",
                "activeBgColor": "false"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "sizeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "sizeTitle",
            "title": "Button size"
          }
        },
        {
          "_icon": "label",
          "text": "sizeLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sizeLabel",
            "text": "Set \"fontSize\" property to set whole button size, and set \"iconSize\" property to set icon size only."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "sizeCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "sizeCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button1",
                "text": "Button 1",
                "icon": "gift",
                "fontSize": ".8em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button2",
                "text": "Button 2",
                "icon": "gift",
                "fontSize": "1.5em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button3",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button3",
                "text": "Big icon only",
                "icon": "gift",
                "iconSize": "2em",
                "iconAlign": "top"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button4",
              "cls": "Wb.Button",
              "properties": {
                "cid": "button4",
                "text": "Big icon only",
                "icon": "gift",
                "iconSize": "2.5em",
                "iconAlign": "top"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "layoutTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "layoutTitle",
            "title": "Button layout"
          }
        },
        {
          "_icon": "label",
          "text": "layoutLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "layoutLabel",
            "text": "Set the \"Layout\" property to arrange buttons."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "layoutCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "layoutCt",
            "frame": "true",
            "layout": "row"
          },
          "items": [
            {
              "_icon": "item",
              "text": "setupItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "setupItem",
                "icon": "setup",
                "iconAlign": "top",
                "text": "Setup",
                "cname": "tool",
                "iconSize": "2.8em"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "container",
              "text": "container1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container1",
                "layout": "row"
              },
              "_expanded": true,
              "hasDupCid": 1,
              "items": [
                {
                  "_icon": "item",
                  "text": "pasteItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "pasteItem",
                    "icon": "paste",
                    "text": "Paste",
                    "cname": "tool",
                    "iconAlign": "top",
                    "iconSize": "2.8em"
                  }
                },
                {
                  "_icon": "container",
                  "text": "container1",
                  "cls": "Wb.Container",
                  "_expanded": true,
                  "properties": {
                    "cid": "container1",
                    "layout": "column",
                    "align": "start"
                  },
                  "hasDupCid": 1,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "cutItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "cutItem",
                        "icon": "cut",
                        "text": "Cut",
                        "cname": "tool"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "copyItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "copyItem",
                        "icon": "copy",
                        "text": "Copy",
                        "cname": "tool"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "deleteItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "deleteItem",
                        "icon": "delete",
                        "text": "Delete",
                        "cname": "tool"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "container",
              "text": "container2",
              "cls": "Wb.Container",
              "_expanded": false,
              "properties": {
                "cid": "container2",
                "layout": "column",
                "align": "center"
              },
              "items": [
                {
                  "_icon": "item",
                  "text": "addItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addItem",
                    "icon": "add",
                    "text": "Add",
                    "cname": "tool"
                  }
                },
                {
                  "_icon": "item",
                  "text": "editItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "editItem",
                    "icon": "edit",
                    "text": "Edit",
                    "cname": "tool"
                  }
                },
                {
                  "_icon": "container",
                  "text": "container1",
                  "cls": "Wb.Container",
                  "_expanded": true,
                  "properties": {
                    "cid": "container1",
                    "layout": "row"
                  },
                  "hasDupCid": 1,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "upItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "upItem",
                        "icon": "up4",
                        "cname": "tool",
                        "shape": "circle"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "rightItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "rightItem",
                        "icon": "right4",
                        "cname": "tool",
                        "shape": "circle"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "downItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "downItem",
                        "icon": "down4",
                        "cname": "tool",
                        "shape": "circle"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "leftItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "leftItem",
                        "icon": "left4",
                        "cname": "tool",
                        "shape": "circle"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "split",
              "text": "setItem",
              "cls": "Wb.SplitButton",
              "properties": {
                "cid": "setItem",
                "icon": "gear",
                "iconSize": "2.8em"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "item1",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item1",
                        "text": "Item 1"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item2",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item2",
                        "text": "Item 2"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item3",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item3",
                        "text": "Item 3"
                      },
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "eventTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "eventTitle",
            "title": "Events"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "eventCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "eventCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "button1",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button1",
                "text": "Click"
              },
              "events": {
                "click": "Wb.tip('click');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "button2",
              "cls": "Wb.Button",
              "_expanded": false,
              "properties": {
                "cid": "button2",
                "active": "true",
                "text": "Toggle"
              },
              "events": {
                "activate": "Wb.tip('activate');",
                "deactivate": "Wb.tip('deactivate');",
                "toggle": "Wb.tip('toggle: ' + active);"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "codingTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "codingTitle",
            "title": "Coding"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "codingCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "codingCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "demoCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "demoCt",
                "layout": "form-compact"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "demoButton",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "demoButton",
                    "text": "Demo Button"
                  }
                }
              ]
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "_expanded": false,
              "properties": {
                "cid": "line1",
                "title": "Demonstrate",
                "dimTitle": "true",
                "dashed": "true"
              }
            },
            {
              "_icon": "container",
              "text": "actionCt",
              "cls": "Wb.Container",
              "_expanded": true,
              "properties": {
                "cid": "actionCt",
                "layout": "form-compact"
              },
              "items": [
                {
                  "_icon": "button",
                  "text": "addBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "addBtn",
                    "text": "Add Button"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoCt.add({ cname: 'button', icon: 'gear', text: 'New Button' });"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setTextIconBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setTextIconBtn",
                    "text": "Set Text Icon"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "let btn = app.demoButton;\nif (btn) {\n  btn.text = 'Changed Text';\n  btn.icon = 'earth';\n}"
                  }
                },
                {
                  "_icon": "button",
                  "text": "disableBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "disableBtn",
                    "text": "Set Disabled"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoButton?.setter('disabled', true); //equals to: if (app.demoButton) app.demoButton.disabled = true;\n//app.demoButton.visible = false; //Hide\n//app.demoButton?.hide(); //Hide"
                  }
                },
                {
                  "_icon": "button",
                  "text": "destroyBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "destroyBtn",
                    "text": "Destroy"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoButton?.destroy();"
                  }
                },
                {
                  "_icon": "button",
                  "text": "addEventBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "addEventBtn",
                    "text": "Add Event"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoBtnOnClick ??= f => Wb.tip('clicked'); //Define function\napp.demoButton?.on('click', app.demoBtnOnClick); //The same function can only be registered once\nWb.tip('Please click Demo Button');"
                  }
                },
                {
                  "_icon": "button",
                  "text": "removeEventBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "removeEventBtn",
                    "text": "Remove Event"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoButton?.un('click', app.demoBtnOnClick);\nWb.tip('Please click Demo Button (Event removed)');"
                  }
                },
                {
                  "_icon": "button",
                  "text": "addListenerBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "addListenerBtn",
                    "text": "Add Listener"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoBtnOnMouseEnter ??= f => Wb.tip('entered'); //Define function\napp.demoButton?.mon('mouseenter', app.demoBtnOnMouseEnter); //Add managed dom listener\nWb.tip('Please move mouse to Demo Button');"
                  }
                },
                {
                  "_icon": "button",
                  "text": "removeListenerBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "removeListenerBtn",
                    "text": "Remove Listener"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.demoButton?.mun('mouseenter', app.demoBtnOnMouseEnter);\nWb.tip('Please move mouse to Demo Button (Listener removed)');"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: create-chart.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.load('wb/libs/echarts.js');\n  let i, theta, r, chart, data = [];\n\n  //Please remove the theme when only used in the serverside\n  chart = echarts.init(null, Wb.get('sys.theme') == 'dark' ? 'dark' : null, {\n    renderer: 'svg',\n    locale: Str.lang == 'zh-cn' ? 'ZH' : 'EN',\n    ssr: true,\n    width: 500,\n    height: 400\n  });\n  for (i = 0; i <= 100; i++) {\n    theta = (i / 100) * 360;\n    r = 5 * (1 + Math.sin((theta / 180) * Math.PI));\n    data.push([r, theta]);\n  }\n  chart.setOption({\n    title: {\n      text: 'Server render'\n    },\n    legend: {\n      data: ['line']\n    },\n    polar: {},\n    tooltip: {\n      trigger: 'axis',\n      axisPointer: {\n        type: 'cross'\n      }\n    },\n    angleAxis: {\n      type: 'value',\n      startAngle: 0\n    },\n    radiusAxis: {},\n    series: [\n      {\n        coordinateSystem: 'polar',\n        name: 'line',\n        type: 'line',\n        data: data\n      }\n    ]\n  });\n  // response.setHeader('Content-Type', 'application/xml');\n  Wb.send(chart.renderToSVGString());\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-chart-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.send({\n    xAxis: {\n      type: 'category',\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    },\n    yAxis: {\n      type: 'value'\n    },\n    series: [\n      {\n        data: [120, 200, 150, 80, 70, 110, 130],\n        type: 'line',\n        symbol: 'triangle',\n        symbolSize: 20,\n        lineStyle: {\n          color: '#5470C6',\n          width: 4,\n          type: 'dashed'\n        },\n        itemStyle: {\n          borderWidth: 3,\n          borderColor: '#EE6666',\n          color: 'yellow'\n        }\n      }\n    ]\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: chart.xwl


{
  "title": "",
  "icon": "chart-bar",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Chart",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "commonTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commonTitle",
            "title": "Common chart"
          },
          "_expanded": true
        },
        {
          "_icon": "chart-bar",
          "text": "commonChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "commonChart",
            "option": "({\n  title: {\n    text: 'ECharts Getting Started Example'\n  },\n  tooltip: {},\n  xAxis: {\n    data: ['shirt', 'cardigan', 'chiffon', 'pants', 'heels', 'socks']\n  },\n  yAxis: {},\n  series: [\n    {\n      name: 'sales',\n      type: 'bar',\n      data: [5, 20, 36, 10, 10, 20]\n    }\n  ]\n})",
            "height": "20em"
          }
        },
        {
          "_icon": "title",
          "text": "manualSetOptionTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "manualSetOptionTitle",
            "title": "Manual setOption"
          },
          "_expanded": true
        },
        {
          "_icon": "chart-bar",
          "text": "manualSetOptionChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "manualSetOptionChart",
            "height": "20em"
          },
          "events": {
            "ready": "// For more parameters please use [this.setOption] method\nthis.option = {\n  title: {\n    text: 'Stacked Area Chart'\n  },\n  tooltip: {\n    trigger: 'axis',\n    axisPointer: {\n      type: 'cross',\n      label: {\n        backgroundColor: '#6a7985'\n      }\n    }\n  },\n  legend: {\n    data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']\n  },\n  toolbox: {\n    feature: {\n      saveAsImage: {}\n    }\n  },\n  grid: {\n    left: '3%',\n    right: '4%',\n    bottom: '3%',\n    containLabel: true\n  },\n  xAxis: [\n    {\n      type: 'category',\n      boundaryGap: false,\n      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n    }\n  ],\n  yAxis: [\n    {\n      type: 'value'\n    }\n  ],\n  series: [\n    {\n      name: 'Email',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [120, 132, 101, 134, 90, 230, 210]\n    },\n    {\n      name: 'Union Ads',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [220, 182, 191, 234, 290, 330, 310]\n    },\n    {\n      name: 'Video Ads',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [150, 232, 201, 154, 190, 330, 410]\n    },\n    {\n      name: 'Direct',\n      type: 'line',\n      stack: 'Total',\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [320, 332, 301, 334, 390, 330, 320]\n    },\n    {\n      name: 'Search Engine',\n      type: 'line',\n      stack: 'Total',\n      label: {\n        show: true,\n        position: 'top'\n      },\n      areaStyle: {},\n      emphasis: {\n        focus: 'series'\n      },\n      data: [820, 932, 901, 934, 1290, 1330, 1320]\n    }\n  ]\n};"
          }
        },
        {
          "_icon": "title",
          "text": "remoteTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "remoteTitle",
            "title": "Load remote data"
          },
          "_expanded": true
        },
        {
          "_icon": "chart-bar",
          "text": "remoteChart",
          "cls": "Wb.Chart",
          "properties": {
            "cid": "remoteChart",
            "height": "20em"
          },
          "events": {
            "ready": "//You can set url property to autoload chart\nthis.load({ url: xpath + '/get-chart-data', mask: { target: this } });"
          }
        },
        {
          "_icon": "button",
          "text": "manualLoadBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "manualLoadBtn",
            "text": "Manual load data"
          },
          "events": {
            "click": "app.remoteChart.clear();\napp.remoteChart.load({ params: { foo: 'bar' } });\n// app.remoteChart.reload();"
          }
        },
        {
          "_icon": "title",
          "text": "serverRenderTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "serverRenderTitle",
            "title": "Server rendering chart"
          }
        },
        {
          "_icon": "label",
          "text": "serverRenderLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "serverRenderLabel",
            "text": "WebBuilder can directly load echarts library in the serverside to create charts, which will bring great convenience. For example, we can use this feature to create charts in the serverside and send them via email."
          }
        },
        {
          "_icon": "panel",
          "text": "serverRenderPanel",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "serverRenderPanel",
            "html": "_$chartSvg$_",
            "layout": "center",
            "minHeight": "8em"
          },
          "events": {
            "ready": "let me = this;\nWb.ajax({\n  url: xpath + '/create-chart',\n  mask: { target: me },\n  success(resp) {\n    me.html = resp;\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: check-slider.xwl


{
  "title": "",
  "icon": "check6",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Check, radio, toggle and slider",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "checkTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "checkTitle",
            "title": "Check"
          }
        },
        {
          "_icon": "container",
          "text": "checkCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "checkCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "check6",
              "text": "commonCheck",
              "cls": "Wb.Check",
              "properties": {
                "cid": "commonCheck",
                "text": "Common check"
              },
              "events": {
                "change": "Wb.tip(this.cid + ' value is ' + value);"
              }
            },
            {
              "_icon": "check6",
              "text": "labelCheck",
              "cls": "Wb.Check",
              "properties": {
                "cid": "labelCheck",
                "text": "With label",
                "label": "Label check",
                "value": "true"
              }
            },
            {
              "_icon": "check6",
              "text": "onlyLabelCheck",
              "cls": "Wb.Check",
              "properties": {
                "cid": "onlyLabelCheck",
                "label": "Only label check"
              },
              "_expanded": true
            },
            {
              "_icon": "check6",
              "text": "onlyLabelEmptyTextCheck",
              "cls": "Wb.Check",
              "properties": {
                "cid": "onlyLabelEmptyTextCheck",
                "label": "Only label check with empty text",
                "showEmptyLabel": "true"
              }
            },
            {
              "_icon": "model",
              "text": "intValueCt",
              "cls": "Wb.ControlCt",
              "properties": {
                "cid": "intValueCt",
                "gap": "1em",
                "showEmptyLabel": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "check6",
                  "text": "intValueCheck",
                  "cls": "Wb.Check",
                  "_expanded": true,
                  "properties": {
                    "cid": "intValueCheck",
                    "returnType": "int",
                    "label": "return int value"
                  }
                },
                {
                  "_icon": "button",
                  "text": "getValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValueBtn",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip('The value is ' + this.previousSibling.value);"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "checkGroupTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "checkGroupTitle",
            "title": "CheckGroup"
          }
        },
        {
          "_icon": "container",
          "text": "checkGroupCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "checkGroupCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonCheckGroupLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonCheckGroupLabel",
                "text": "Common checkGroup"
              }
            },
            {
              "_icon": "container",
              "text": "commonCheckCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "commonCheckCt",
                "gap": "1em",
                "layout": "row"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "checks",
                  "text": "commonCheckGroup",
                  "cls": "Wb.CheckGroup",
                  "properties": {
                    "cid": "commonCheckGroup"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "_icon": "check6",
                      "text": "check1",
                      "cls": "Wb.Check",
                      "properties": {
                        "cid": "check1",
                        "label": "item 1"
                      }
                    },
                    {
                      "_icon": "check6",
                      "text": "check2",
                      "cls": "Wb.Check",
                      "properties": {
                        "cid": "check2",
                        "label": "item 2",
                        "value": "true"
                      }
                    },
                    {
                      "_icon": "check6",
                      "text": "check3",
                      "cls": "Wb.Check",
                      "_expanded": true,
                      "properties": {
                        "cid": "check3",
                        "label": "item 3",
                        "value": "true"
                      }
                    }
                  ]
                },
                {
                  "_icon": "button",
                  "text": "getValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValueBtn",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip(Wb.encode(app.commonCheckGroup.value));"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueBtn",
                    "text": "Set value"
                  },
                  "events": {
                    "click": "app.commonCheckGroup.setValue({ check1: true, check3: true });"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "allCheckGroupLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "allCheckGroupLabel",
                "text": "Return all and return array value checkGroup"
              }
            },
            {
              "_icon": "container",
              "text": "allCheckCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "allCheckCt",
                "gap": "1em",
                "layout": "row"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "checks",
                  "text": "allCheckGroup",
                  "cls": "Wb.CheckGroup",
                  "properties": {
                    "cid": "allCheckGroup",
                    "returnAll": "true",
                    "returnArray": "true"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "_icon": "check6",
                      "text": "check1",
                      "cls": "Wb.Check",
                      "properties": {
                        "cid": "check1",
                        "label": "item 1"
                      }
                    },
                    {
                      "_icon": "check6",
                      "text": "check2",
                      "cls": "Wb.Check",
                      "properties": {
                        "cid": "check2",
                        "label": "item 2",
                        "value": "true"
                      }
                    },
                    {
                      "_icon": "check6",
                      "text": "check3",
                      "cls": "Wb.Check",
                      "_expanded": true,
                      "properties": {
                        "cid": "check3",
                        "label": "item 3",
                        "value": "true"
                      }
                    }
                  ]
                },
                {
                  "_icon": "button",
                  "text": "getValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValueBtn",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip(Wb.encode(app.allCheckGroup.value));"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueObjectBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueObjectBtn",
                    "text": "Set value by object"
                  },
                  "events": {
                    "click": "app.allCheckGroup.setValue({ check1: true, check3: 'true' });"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueArrayBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueArrayBtn",
                    "text": "Set value by array"
                  },
                  "events": {
                    "click": "app.allCheckGroup.setValue([false, 1, 'true']);"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "button",
                  "text": "setValueCidsBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueCidsBtn",
                    "text": "Set value by cids"
                  },
                  "events": {
                    "click": "app.allCheckGroup.setValue(['check1', 'check3'] );"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "radioTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "radioTitle",
            "title": "Radio"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "radioCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "radioCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "check2",
              "text": "radio1",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "radio1",
                "label": "item 1",
                "group": "group1"
              }
            },
            {
              "_icon": "check2",
              "text": "radio2",
              "cls": "Wb.Radio",
              "_expanded": false,
              "properties": {
                "cid": "radio2",
                "label": "item 2",
                "group": "group1"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "check2",
              "text": "radio3",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "radio3",
                "label": "item 3",
                "group": "group2"
              }
            },
            {
              "_icon": "check2",
              "text": "radio4",
              "cls": "Wb.Radio",
              "_expanded": false,
              "properties": {
                "cid": "radio4",
                "label": "item 4",
                "group": "group2",
                "value": "true"
              }
            },
            {
              "_icon": "check2",
              "text": "radio5",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "radio5",
                "label": "item 5",
                "group": "group2"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "radioGroupTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "radioGroupTitle",
            "title": "RadioGroup"
          }
        },
        {
          "_icon": "container",
          "text": "radioGroupCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "radioGroupCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonRadioGroupLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonRadioGroupLabel",
                "text": "Common radioGroup"
              }
            },
            {
              "_icon": "container",
              "text": "commonRadioCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "commonRadioCt",
                "gap": "1em",
                "layout": "row"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "radios",
                  "text": "commonRadioGroup",
                  "cls": "Wb.RadioGroup",
                  "_expanded": true,
                  "properties": {
                    "cid": "commonRadioGroup"
                  },
                  "items": [
                    {
                      "_icon": "check2",
                      "text": "radio1",
                      "cls": "Wb.Radio",
                      "properties": {
                        "cid": "radio1",
                        "label": "item 1"
                      }
                    },
                    {
                      "_icon": "check2",
                      "text": "radio2",
                      "cls": "Wb.Radio",
                      "properties": {
                        "cid": "radio2",
                        "label": "item 2"
                      }
                    },
                    {
                      "_icon": "check2",
                      "text": "radio3",
                      "cls": "Wb.Radio",
                      "_expanded": true,
                      "properties": {
                        "cid": "radio3",
                        "label": "item 3"
                      }
                    }
                  ]
                },
                {
                  "_icon": "button",
                  "text": "getValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValueBtn",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip(app.commonRadioGroup.value);"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueBtn",
                    "text": "Set value"
                  },
                  "events": {
                    "click": "app.commonRadioGroup.setValue(2);"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "returnCidRadioGroupLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "returnCidRadioGroupLabel",
                "text": "Return cid as value radioGroup"
              }
            },
            {
              "_icon": "container",
              "text": "returnNameRadioCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "returnNameRadioCt",
                "gap": "1em",
                "layout": "row"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "radios",
                  "text": "returnNameRadioGroup",
                  "cls": "Wb.RadioGroup",
                  "_expanded": true,
                  "properties": {
                    "cid": "returnNameRadioGroup",
                    "returnName": "true"
                  },
                  "items": [
                    {
                      "_icon": "check2",
                      "text": "radio1",
                      "cls": "Wb.Radio",
                      "properties": {
                        "cid": "radio1",
                        "label": "item 1",
                        "value": "true"
                      }
                    },
                    {
                      "_icon": "check2",
                      "text": "radio2",
                      "cls": "Wb.Radio",
                      "properties": {
                        "cid": "radio2",
                        "label": "item 2"
                      }
                    },
                    {
                      "_icon": "check2",
                      "text": "radio3",
                      "cls": "Wb.Radio",
                      "_expanded": true,
                      "properties": {
                        "cid": "radio3",
                        "label": "item 3"
                      }
                    }
                  ]
                },
                {
                  "_icon": "button",
                  "text": "getValueBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValueBtn",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip(app.returnNameRadioGroup.value);"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueByIndexBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueByIndexBtn",
                    "text": "Set value by index"
                  },
                  "events": {
                    "click": "app.returnNameRadioGroup.setValue(2);"
                  }
                },
                {
                  "_icon": "button",
                  "text": "setValueByCidBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "setValueByCidBtn",
                    "text": "Set value by cid"
                  },
                  "events": {
                    "click": "app.returnNameRadioGroup.setValue('radio2');"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "toggleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "toggleTitle",
            "title": "Toggle"
          }
        },
        {
          "_icon": "container",
          "text": "toggleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "toggleCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "switcher-on",
              "text": "commonToggle",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "commonToggle",
                "text": "Common toggle"
              },
              "events": {
                "change": "Wb.tip(this.cid + ' is ' + value);"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "prefixSuffixToggle",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "prefixSuffixToggle",
                "text": "Prefix suffix",
                "prefix": "Prefix text",
                "suffix": "Suffix text"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "dragToToggle",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "dragToToggle",
                "text": "Drag to toggle",
                "clickToggle": "false"
              },
              "_expanded": true
            },
            {
              "_icon": "switcher-on",
              "text": "alignRightToggle",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "alignRightToggle",
                "text": "Align right toggle",
                "toggleAlign": "end"
              }
            },
            {
              "_icon": "switcher-on",
              "text": "verticalToggle",
              "cls": "Wb.Toggle",
              "properties": {
                "cid": "verticalToggle",
                "text": "Vertical toggle",
                "vertical": "true"
              }
            },
            {
              "_icon": "model",
              "text": "returnIntCt",
              "cls": "Wb.ControlCt",
              "properties": {
                "cid": "returnIntCt",
                "gap": "1em",
                "text": "Return int value"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "switcher-on",
                  "text": "returnIntToggle",
                  "cls": "Wb.Toggle",
                  "properties": {
                    "cid": "returnIntToggle",
                    "returnType": "int"
                  }
                },
                {
                  "_icon": "button",
                  "text": "getValue",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "getValue",
                    "text": "Get value"
                  },
                  "events": {
                    "click": "Wb.tip(this.cid + ' value is ' + this.previousSibling.value);"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "sliderTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "sliderTitle",
            "title": "Slider"
          }
        },
        {
          "_icon": "container",
          "text": "sliderCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "sliderCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "slidebar",
              "text": "commonSlider",
              "cls": "Wb.Slider",
              "properties": {
                "cid": "commonSlider",
                "text": "Common slider",
                "value": "30"
              },
              "events": {
                "startdrag": "Wb.tip('Start drag at ' + value);",
                "enddrag": "Wb.tip('End drag at ' + value);"
              }
            },
            {
              "_icon": "slidebar",
              "text": "multiSlider",
              "cls": "Wb.Slider",
              "properties": {
                "cid": "multiSlider",
                "text": "Multi slider",
                "value": "[10, 50, 80]",
                "suffix": "Suffix",
                "prefix": "Prefix"
              },
              "events": {
                "enddrag": "Wb.tip('The values are ' + Wb.encode(value));"
              }
            },
            {
              "_icon": "model",
              "text": "verticalSliderCt",
              "cls": "Wb.ControlCt",
              "properties": {
                "cid": "verticalSliderCt",
                "text": "Vertical slider",
                "layout": "row",
                "gap": "2em",
                "height": "10em",
                "align": "stretch"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "slidebar",
                  "text": "verticalSlider",
                  "cls": "Wb.Slider",
                  "properties": {
                    "cid": "verticalSlider",
                    "vertical": "true",
                    "value": "20",
                    "text": "Text",
                    "labelUp": "true",
                    "labelAlign": "center",
                    "labelSeparator": "false"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "slidebar",
                  "text": "verticalMulSlider",
                  "cls": "Wb.Slider",
                  "properties": {
                    "cid": "verticalMulSlider",
                    "vertical": "true",
                    "value": "[30, 70]",
                    "prefix": "Prefix",
                    "suffix": "Suffix",
                    "labelUp": "true"
                  },
                  "events": {
                    "enddrag": "Wb.tip('The values are ' + Wb.encode(value));"
                  },
                  "_expanded": true
                }
              ]
            },
            {
              "_icon": "slidebar",
              "text": "justifyLeftVerticalSlider",
              "cls": "Wb.Slider",
              "properties": {
                "cid": "justifyLeftVerticalSlider",
                "vertical": "true",
                "justifySelf": "start",
                "value": "20",
                "text": "Justify left",
                "height": "6em"
              }
            },
            {
              "_icon": "slidebar",
              "text": "volumeSlider",
              "cls": "Wb.Slider",
              "properties": {
                "cid": "volumeSlider",
                "text": "Volume",
                "suffix": "true",
                "maxValue": "100",
                "suffixWidth": "3em",
                "value": "50"
              },
              "events": {
                "change": "Wb.toast('Volumn is ' + value);"
              },
              "_expanded": true
            },
            {
              "_icon": "slidebar",
              "text": "miscSlider",
              "cls": "Wb.Slider",
              "properties": {
                "cid": "miscSlider",
                "text": "Misc slider",
                "prefix": "100",
                "suffix": "500",
                "maxValue": "500",
                "step": "10",
                "value": "200",
                "minValue": "100",
                "tipRender": "return 'Custom render ' + value;"
              }
            },
            {
              "_icon": "check3",
              "text": "areaSlider1",
              "cls": "Wb.AreaSlider",
              "properties": {
                "cid": "areaSlider1",
                "text": "Area slider",
                "width": "30em",
                "height": "10em",
                "value": "[20, 30]",
                "maxValue": "[300, 200]"
              },
              "events": {
                "enddrag": "Wb.toast('The value is ' + Wb.encode(value));"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: code-editor.xwl


{
  "title": "",
  "icon": "edit",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "CodeEditor",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "jsEditorTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "jsEditorTitle",
            "title": "JavaScript editor"
          }
        },
        {
          "_icon": "edit",
          "text": "jsEditor",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "jsEditor",
            "value": "Cls['Wb.Button'] = class button extends Wb.mixin.Icon(Wb.Component) {\n  static configs = {\n    tabIndex: Wb.isTouch ? undefined : 0,\n    rippleOnClick: true\n  };\n  /***/\n  init(configs) {\n    let me = this;\n    super.init(configs);\n    me.on('click', me.onClickEvent);\n    me.mon({ [me.tapClick ? Event.tapDownName : 'click']: me.onClick, keydown: me.onKeyDown });\n  }\n}",
            "height": "18em"
          }
        },
        {
          "_icon": "title",
          "text": "sqlEditorTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "sqlEditorTitle",
            "title": "SQL editor"
          }
        },
        {
          "_icon": "edit",
          "text": "sqlEditor",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "sqlEditor",
            "value": "select a.field1, b.field2 from table1 a, table2 b where a.sid=b.sid\norder by a.field1",
            "height": "3em",
            "language": "sql"
          }
        },
        {
          "_icon": "title",
          "text": "htmlEditorTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "htmlEditorTitle",
            "title": "HTML editor without border"
          }
        },
        {
          "_icon": "edit",
          "text": "htmlEditor",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "htmlEditor",
            "value": "<!DOCTYPE html>\n<head>\n<meta charset=\"UTF-8\"/>\n<title>html</title>\n<link rel=\"icon\" href=\"wb/images/app/favicon.ico\"/>\n<link rel=\"stylesheet\" href=\"wb/css/iconfont.css\"/>\n<link rel=\"stylesheet\" href=\"wb/css/dark-wb.css\"/>\n<script src=\"wb/js/locale/wb-zh-cn.js\"></script>\n<script src=\"wb/js/wb.js\"></script>\n<script src=\"wb/js/wb-client.js\"></script>\n</head>\n<body>\n<script type=\"module\">\nWb.onLoad(f => {\n});\n</script>\n</body>\n</html>",
            "height": "18em",
            "language": "html",
            "wrapBorder": "false"
          }
        },
        {
          "_icon": "label",
          "text": "otherLanguageLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "otherLanguageLabel",
            "text": "For other languages, please set the language property."
          }
        }
      ]
    }
  ]
}

File name: container.xwl


{
  "title": "",
  "icon": "container",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Containers",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "containerTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "containerTitle",
            "title": "Container"
          }
        },
        {
          "_icon": "container",
          "text": "containerCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "containerCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "commonCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "commonCt",
                "layout": "grid1",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "line",
                  "text": "descLine",
                  "cls": "Wb.Line",
                  "properties": {
                    "cid": "descLine",
                    "title": "Common Container"
                  }
                },
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "text"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "number-edit",
                  "text": "number1",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "number1",
                    "text": "Custom Label Separator",
                    "labelSeparator": "-",
                    "labelUp": "true"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "date1",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "date1",
                    "text": "No Label Separator",
                    "labelUp": "true",
                    "labelSeparator": "false"
                  }
                }
              ]
            },
            {
              "_icon": "container",
              "text": "customLabelCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "customLabelCt",
                "layout": "grid1",
                "frame": "true",
                "labelUp": "true",
                "labelAlign": "center"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "line",
                  "text": "descLine",
                  "cls": "Wb.Line",
                  "properties": {
                    "cid": "descLine",
                    "title": "Custom Label Align"
                  }
                },
                {
                  "_icon": "text",
                  "text": "defaultText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "defaultText",
                    "text": "Default to parent"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "leftCommonText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "leftCommonText",
                    "text": "Left Common",
                    "labelAlign": "left",
                    "labelUp": "false"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "rightCommonText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "rightCommonText",
                    "text": "Right Common",
                    "labelAlign": "right",
                    "labelUp": "false"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "leftAboveText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "leftAboveText",
                    "text": "Left Above",
                    "labelAlign": "left",
                    "labelUp": "true"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "rightAboveText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "rightAboveText",
                    "text": "Right Above",
                    "labelAlign": "right",
                    "labelUp": "true"
                  },
                  "_expanded": true
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "panelTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "panelTitle",
            "title": "Panel"
          }
        },
        {
          "_icon": "container",
          "text": "panelCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "panelCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "panel",
              "text": "commonPanel",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "commonPanel",
                "layout": "grid1",
                "html": "Common Panel"
              }
            },
            {
              "_icon": "panel",
              "text": "titledPanel",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "titledPanel",
                "title": "Titled Panel",
                "collapsible": "true",
                "closable": "true",
                "layout": "grid1",
                "width": "30em",
                "maximizable": "true",
                "minimizable": "true",
                "tagProperties": "({\n  minimize() {\n    Wb.tip('Do minimize');\n  }\n})"
              },
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "text"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "number1",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "number1",
                    "text": "number"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "date1",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "date1",
                    "text": "date"
                  }
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "dockedBarsPanel",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "dockedBarsPanel",
                "frame": "true",
                "title": "Panel with docked bars",
                "layout": "grid1",
                "maximizable": "true",
                "buttonAlign": "center",
                "defaults": "({ labelWidth: '5em' })",
                "height": "20em",
                "icon": "address-book",
                "minButtonWidth": "6em",
                "collapsible": "true"
              },
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "tbar"
                  },
                  "text": "tbar",
                  "_expanded": true,
                  "_icon": "array",
                  "items": [
                    {
                      "cls": "Wb.Toolbar",
                      "properties": {
                        "cid": "toolbar1"
                      },
                      "text": "toolbar1",
                      "_expanded": true,
                      "_icon": "toolbar",
                      "items": [
                        {
                          "_icon": "item",
                          "text": "addItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "addItem",
                            "icon": "add1",
                            "text": "Add"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "delItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "delItem",
                            "icon": "delete",
                            "text": "Delete"
                          }
                        }
                      ]
                    },
                    {
                      "_icon": "toolbar",
                      "text": "toolbar2",
                      "cls": "Wb.Toolbar",
                      "properties": {
                        "cid": "toolbar2",
                        "fontSize": ".8em"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "_icon": "label",
                          "text": "label1",
                          "cls": "Wb.Label",
                          "_expanded": true,
                          "properties": {
                            "cid": "label1",
                            "text": "Address: "
                          }
                        },
                        {
                          "_icon": "text",
                          "text": "text1",
                          "cls": "Wb.Text",
                          "properties": {
                            "cid": "text1",
                            "flex": "1"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "playBtn",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "playBtn",
                            "icon": "play",
                            "alignSelf": "stretch"
                          }
                        }
                      ]
                    }
                  ]
                },
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "text1"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "text",
                  "text": "text2",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text2",
                    "text": "text2"
                  }
                },
                {
                  "_icon": "text",
                  "text": "text3",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text3",
                    "text": "text3"
                  }
                },
                {
                  "_icon": "text",
                  "text": "text4",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text4",
                    "text": "text4"
                  }
                },
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "buttons"
                  },
                  "text": "buttons",
                  "_expanded": true,
                  "_icon": "array",
                  "items": [
                    {
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "okBtn",
                        "text": "@Str.ok",
                        "type": "primary"
                      },
                      "text": "okBtn",
                      "_expanded": true,
                      "_icon": "button"
                    },
                    {
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "cancelBtn",
                        "text": "@Str.cancel"
                      },
                      "text": "cancelBtn",
                      "_expanded": true,
                      "_icon": "button"
                    }
                  ]
                },
                {
                  "cls": "Wb.Toolbar",
                  "properties": {
                    "cid": "bbar",
                    "isProperty": "true",
                    "justify": "end"
                  },
                  "text": "bbar",
                  "_expanded": true,
                  "_icon": "toolbar",
                  "items": [
                    {
                      "_icon": "item",
                      "text": "menuItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "menuItem",
                        "icon": "menu"
                      },
                      "items": [
                        {
                          "_icon": "menu2",
                          "text": "menu",
                          "cls": "Wb.Menu",
                          "properties": {
                            "cid": "menu",
                            "isProperty": "true"
                          },
                          "_expanded": true,
                          "items": [
                            {
                              "_icon": "item",
                              "text": "item1",
                              "cls": "Wb.Item",
                              "_expanded": true,
                              "properties": {
                                "cid": "item1",
                                "text": "item1"
                              }
                            },
                            {
                              "_icon": "item",
                              "text": "item2",
                              "cls": "Wb.Item",
                              "_expanded": false,
                              "properties": {
                                "cid": "item2",
                                "text": "item2"
                              }
                            }
                          ]
                        }
                      ]
                    }
                  ]
                },
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "tools"
                  },
                  "text": "tools",
                  "_expanded": true,
                  "_icon": "array",
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "properties": {
                        "cid": "setItem",
                        "icon": "gear",
                        "tip": "Set"
                      },
                      "text": "setItem",
                      "_expanded": true,
                      "_icon": "item"
                    },
                    {
                      "cls": "Wb.Item",
                      "properties": {
                        "cid": "printItem",
                        "icon": "print",
                        "tip": "Print"
                      },
                      "text": "printItem",
                      "_expanded": true,
                      "_icon": "item"
                    },
                    {
                      "cls": "Wb.Item",
                      "properties": {
                        "cid": "pinItem",
                        "icon": "pin",
                        "tip": "Pin"
                      },
                      "text": "pinItem",
                      "_expanded": true,
                      "_icon": "item"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "treeStyleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "treeStyleTitle",
            "title": "Tree style panels"
          }
        },
        {
          "_icon": "container",
          "text": "treeStyleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "treeStyleCt",
            "frame": "true",
            "layout": "column"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "panel",
              "text": "panel1",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "panel1",
                "layout": "grid1",
                "collapsible": "tree",
                "title": "Panel1",
                "textSelectable": "false"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1"
                  }
                },
                {
                  "_icon": "text",
                  "text": "text2",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text2"
                  }
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "panel2",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "panel2",
                "collapsible": "tree",
                "height": "5em",
                "text": "Panel2",
                "title": "Panel2",
                "collapsed": "true",
                "textSelectable": "false"
              }
            },
            {
              "_icon": "panel",
              "text": "panel3",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "panel3",
                "collapsible": "tree",
                "height": "8em",
                "text": "Panel3",
                "title": "Panel3",
                "textSelectable": "false"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "accordionStyleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "accordionStyleTitle",
            "title": "Accordion style panels"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "accordionStyleCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "accordionStyleCt",
            "frame": "true",
            "layout": "column",
            "height": "20em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "panel",
              "text": "panel1",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "panel1",
                "layout": "grid1",
                "collapsible": "accordion",
                "title": "Panel1",
                "textSelectable": "false",
                "flex": "1",
                "icon": "gear"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1"
                  }
                },
                {
                  "_icon": "text",
                  "text": "text2",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text2"
                  }
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "panel2",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "panel2",
                "collapsible": "accordion",
                "text": "Panel2",
                "title": "Panel2",
                "textSelectable": "false",
                "flex": "1",
                "collapsed": "true",
                "icon": "excel"
              }
            },
            {
              "_icon": "panel",
              "text": "panel3",
              "cls": "Wb.Panel",
              "_expanded": true,
              "properties": {
                "cid": "panel3",
                "collapsible": "accordion",
                "text": "Panel3",
                "title": "Panel3",
                "textSelectable": "false",
                "flex": "1",
                "collapsed": "true",
                "icon": "word"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "plainTitlePanelBlocksTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "plainTitlePanelBlocksTitle",
            "title": "Plain title panel blocks"
          }
        },
        {
          "_icon": "label",
          "text": "plainTitlePanelBlocksLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "plainTitlePanelBlocksLabel",
            "text": "A list of panels organized by blocks"
          }
        },
        {
          "_icon": "container",
          "text": "plainTitlePanelBlocksCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "plainTitlePanelBlocksCt",
            "layout": "grid1",
            "frame": "true",
            "gap": "1em",
            "padding": "1em",
            "background": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "panel",
              "text": "formPanel",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "formPanel",
                "plainTitle": "true",
                "title": "Block 1 - Form panel1",
                "icon": "form",
                "layout": "grid1",
                "roundBorder": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "Text"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "number1",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "number1",
                    "text": "Number"
                  }
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "formPanel2",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "formPanel2",
                "plainTitle": "true",
                "title": "Block 2 - Form panel2",
                "icon": "word",
                "layout": "grid1",
                "roundBorder": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "Text"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "number1",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "number1",
                    "text": "Number"
                  }
                }
              ]
            },
            {
              "_icon": "panel",
              "text": "formPanel3",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "formPanel3",
                "plainTitle": "true",
                "title": "Block 3 - Form panel3",
                "icon": "excel",
                "layout": "grid1",
                "roundBorder": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "Text"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "number1",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "number1",
                    "text": "Number"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "fieldsetTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "fieldsetTitle",
            "title": "Fieldset"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "fieldsetCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "fieldsetCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "fieldset",
              "text": "comonFieldset",
              "cls": "Wb.Fieldset",
              "properties": {
                "cid": "comonFieldset",
                "title": "Common Fieldset",
                "layout": "grid"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "nameText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "nameText",
                    "text": "Name"
                  }
                },
                {
                  "_icon": "text",
                  "text": "addrText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "addrText",
                    "text": "Address"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "birthDate",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "birthDate",
                    "text": "Birth date"
                  }
                }
              ]
            },
            {
              "_icon": "fieldset",
              "text": "collapsibleFieldset",
              "cls": "Wb.Fieldset",
              "properties": {
                "cid": "collapsibleFieldset",
                "title": "Collapsible Fieldset",
                "layout": "grid",
                "collapsible": "true",
                "icon": "id-card",
                "labelAlign": "left"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "nameText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "nameText",
                    "text": "Name"
                  }
                },
                {
                  "_icon": "text",
                  "text": "addrText",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "addrText",
                    "text": "Address"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "birthDate",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "birthDate",
                    "text": "Birth date"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "tabTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tabTitle",
            "title": "Tab"
          }
        },
        {
          "_icon": "container",
          "text": "tabCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "tabCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonLabel",
                "text": "Common Tab"
              }
            },
            {
              "_icon": "tab",
              "text": "commonTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "commonTab",
                "height": "10em",
                "width": "48em"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "iconCard",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "iconCard",
                    "icon": "home",
                    "title": "Icon Card"
                  }
                },
                {
                  "_icon": "card",
                  "text": "imageIconCard",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "imageIconCard",
                    "img": "support",
                    "title": "Image Icon",
                    "closable": "null"
                  }
                },
                {
                  "_icon": "card",
                  "text": "loadingCard",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "loadingCard",
                    "icon": "loading",
                    "title": "Loading",
                    "spin": "true"
                  }
                },
                {
                  "_icon": "card",
                  "text": "disabledCard",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "disabledCard",
                    "disabled": "true",
                    "title": "Disabled",
                    "icon": "calendar"
                  }
                },
                {
                  "_icon": "card",
                  "text": "noSortableCard",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "noSortableCard",
                    "title": "No Sortable",
                    "sortable": "false",
                    "icon": "🌻"
                  }
                },
                {
                  "_icon": "card",
                  "text": "overflowedCard",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "overflowedCard",
                    "title": "Overflowed",
                    "tabTip": "This tab card is overflowed"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "miscTabLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "miscTabLabel",
                "text": "Miscellaneous Tab"
              },
              "_expanded": true
            },
            {
              "_icon": "tab",
              "text": "miscTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "miscTab",
                "height": "10em",
                "width": "30em",
                "resizable": "true",
                "dblclickFull": "true",
                "shrinkTabButton": "true",
                "clipTabText": "true",
                "frame": "true"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "card1",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card1",
                    "title": "Cliped Text",
                    "html": "Try resizing small<br>\nDouble click tab button",
                    "layout": "center"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card2",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card2",
                    "title": "Shrink Tab Button"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card3",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card3",
                    "title": "Card3"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card4",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card4",
                    "title": "Card4"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "wrappedTabLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "wrappedTabLabel",
                "text": "Wrapped Tab Buttons"
              },
              "_expanded": true
            },
            {
              "_icon": "tab",
              "text": "wrappedTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "wrappedTab",
                "height": "10em",
                "width": "20em",
                "resizable": "true",
                "wrapTab": "true",
                "frame": "true"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "card1",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "card1",
                    "title": "Card1"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card2",
                  "cls": "Wb.Card",
                  "properties": {
                    "cid": "card2",
                    "title": "Card2"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card3",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card3",
                    "title": "Card3"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card4",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card4",
                    "title": "Card4"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card5",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card5",
                    "title": "Card5"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "bottomLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "bottomLabel",
                "text": "Bottom Tab"
              }
            },
            {
              "_icon": "tab",
              "text": "bottomTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "bottomTab",
                "tabPosition": "bottom",
                "height": "10em"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "card1",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card1",
                    "title": "Card1"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card2",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card2",
                    "title": "Card2"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card3",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card3",
                    "title": "Card3"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "leftLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "leftLabel",
                "text": "Left Tab"
              }
            },
            {
              "_icon": "tab",
              "text": "leftTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "leftTab",
                "tabPosition": "left",
                "height": "10em"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "card1",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card1",
                    "title": "Card1"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card2",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card2",
                    "title": "Card2"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card3",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card3",
                    "title": "Card3"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "rightLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "rightLabel",
                "text": "Right Tab"
              }
            },
            {
              "_icon": "tab",
              "text": "rightTab",
              "cls": "Wb.Tab",
              "_expanded": true,
              "properties": {
                "cid": "rightTab",
                "tabPosition": "right",
                "height": "10em"
              },
              "items": [
                {
                  "_icon": "card",
                  "text": "card1",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card1",
                    "title": "Card1"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card2",
                  "cls": "Wb.Card",
                  "_expanded": false,
                  "properties": {
                    "cid": "card2",
                    "title": "Card2"
                  }
                },
                {
                  "_icon": "card",
                  "text": "card3",
                  "cls": "Wb.Card",
                  "_expanded": true,
                  "properties": {
                    "cid": "card3",
                    "title": "Card3"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: date.xwl


{
  "title": "",
  "icon": "calendar",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Date and Time",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "dateTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dateTitle",
            "title": "Date"
          }
        },
        {
          "_icon": "container",
          "text": "dateCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "dateCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "calendar",
              "text": "commonDate",
              "cls": "Wb.Date",
              "properties": {
                "cid": "commonDate",
                "text": "Common"
              }
            },
            {
              "_icon": "calendar",
              "text": "constraintDate",
              "cls": "Wb.Date",
              "_expanded": true,
              "properties": {
                "cid": "constraintDate",
                "text": "Constraint Value",
                "maxValue": "@new Date().addDay(7)",
                "minValue": "@new Date().addDay(-7)"
              }
            },
            {
              "_icon": "model",
              "text": "dateRangeControlCt",
              "cls": "Wb.ControlCt",
              "properties": {
                "cid": "dateRangeControlCt",
                "text": "Date Range",
                "layout": "row",
                "gap": "1em"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "calendar",
                  "text": "fromDate",
                  "cls": "Wb.Date",
                  "_expanded": true,
                  "properties": {
                    "cid": "fromDate"
                  }
                },
                {
                  "_icon": "label",
                  "text": "toLabel",
                  "cls": "Wb.Label",
                  "properties": {
                    "cid": "toLabel",
                    "text": "to"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "toDate",
                  "cls": "Wb.Date",
                  "_expanded": false,
                  "properties": {
                    "cid": "toDate",
                    "beginDate": "fromDate",
                    "rangeOffset": "3"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "timeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "timeTitle",
            "title": "Time"
          }
        },
        {
          "_icon": "container",
          "text": "timeCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "timeCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "time",
              "text": "commonTime",
              "cls": "Wb.Time",
              "properties": {
                "cid": "commonTime",
                "text": "Common"
              }
            },
            {
              "_icon": "time",
              "text": "localFormatTime",
              "cls": "Wb.Time",
              "properties": {
                "cid": "localFormatTime",
                "localFormat": "timeText12",
                "text": "Local Format",
                "clearButton": "true",
                "width": "22em"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "dateTimeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dateTimeTitle",
            "title": "Datetime"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "datetimeCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "datetimeCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "dt-picker",
              "text": "commonDateTime",
              "cls": "Wb.Datetime",
              "properties": {
                "cid": "commonDateTime",
                "text": "Common"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "othersTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "othersTitle",
            "title": "Others"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "othersCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "othersCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "month",
              "text": "yearMonth1",
              "cls": "Wb.YearMonth",
              "properties": {
                "cid": "yearMonth1",
                "text": "Year Month"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "promptTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "promptTitle",
            "title": "Display date/time prompt window through methods"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "tipLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "tipLabel",
            "text": "There are different dialog representations on the desktop and touch mode."
          }
        },
        {
          "_icon": "container",
          "text": "promptCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "promptCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "dateAutoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dateAutoBtn",
                "text": "Prompt Date auto"
              },
              "events": {
                "click": "// Auto select drawer or dialog base on touch or desktop\nWb.promptDate(date => Wb.tip(date.dateText), new Date(), 'Select Date');"
              }
            },
            {
              "_icon": "button",
              "text": "dateDrawerBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dateDrawerBtn",
                "text": "Prompt Date Drawer"
              },
              "events": {
                "click": "Wb.promptDate(date => Wb.tip(date.dateText), new Date(), 'Select Date', true);"
              }
            },
            {
              "_icon": "button",
              "text": "dateDialogBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dateDialogBtn",
                "text": "Prompt Date Dialog"
              },
              "events": {
                "click": "Wb.promptDate(date => Wb.tip(date.dateText), new Date(), 'Select Date', false);"
              }
            },
            {
              "_icon": "button",
              "text": "timeDrawerBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "timeDrawerBtn",
                "text": "Prompt Time Drawer"
              },
              "events": {
                "click": "Wb.promptTime(date => Wb.tip(date.timeText), null, null, true);"
              }
            },
            {
              "_icon": "button",
              "text": "timeDialogBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "timeDialogBtn",
                "text": "Prompt Time Dialog"
              },
              "events": {
                "click": "Wb.promptTime(date => Wb.tip(date.timeText), null, null, false);"
              }
            },
            {
              "_icon": "button",
              "text": "dateTimeDrawerBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dateTimeDrawerBtn",
                "text": "Prompt Datetime Drawer"
              },
              "events": {
                "click": "Wb.promptDateTime(date => Wb.tip(date.dateTimeText), null, null, true);"
              }
            },
            {
              "_icon": "button",
              "text": "dateTimeDialogBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dateTimeDialogBtn",
                "text": "Prompt Datetime Dialog"
              },
              "events": {
                "click": "Wb.promptDateTime(date => Wb.tip(date.dateTimeText), null, null, false);"
              }
            },
            {
              "_icon": "button",
              "text": "monthDrawerBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "monthDrawerBtn",
                "text": "Prompt Month Drawer"
              },
              "events": {
                "click": "Wb.promptMonth(date => Wb.tip(date.format('y-M')), null, null, true);"
              }
            },
            {
              "_icon": "button",
              "text": "monthDialogBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "monthDialogBtn",
                "text": "Prompt Month Dialog"
              },
              "events": {
                "click": "Wb.promptMonth(date => Wb.tip(date.format('y-M')), null, null, false);"
              }
            },
            {
              "_icon": "button",
              "text": "preventCloseBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "preventCloseBtn",
                "text": "Prevent Close"
              },
              "events": {
                "click": "Wb.promptDate(date => {\n  Wb.tip('Close prevented');\n  return false;\n});"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: decoration.xwl


{
  "title": "",
  "icon": "label",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Label and decorative components",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "labelTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "labelTitle",
            "title": "Label"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "labelCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "labelCt",
            "layout": "form",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "label",
              "text": "textLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "textLabel",
                "text": "Text label"
              }
            },
            {
              "_icon": "label",
              "text": "htmlLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "htmlLabel",
                "html": "HTML <span class=\"w-error-color\">label</span>"
              }
            },
            {
              "_icon": "label",
              "text": "linkLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "linkLabel",
                "target": "_blank",
                "href": "https://www.geejing.com",
                "text": "Link label"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "boxLabelTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "boxLabelTitle",
            "title": "BoxLabel"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "boxLabelCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "boxLabelCt",
            "layout": "form",
            "frame": "true",
            "defaults": "({ width: '10em', height: '5em' })"
          },
          "items": [
            {
              "_icon": "box-label",
              "text": "centerLabel",
              "cls": "Wb.BoxLabel",
              "properties": {
                "cid": "centerLabel",
                "text": "Center",
                "frame": "true"
              }
            },
            {
              "_icon": "box-label",
              "text": "topLeftLabel",
              "cls": "Wb.BoxLabel",
              "properties": {
                "cid": "topLeftLabel",
                "text": "Top left",
                "align": "topLeft",
                "frame": "true"
              }
            },
            {
              "_icon": "box-label",
              "text": "bottomRightLabel",
              "cls": "Wb.BoxLabel",
              "properties": {
                "cid": "bottomRightLabel",
                "text": "Bottom right",
                "align": "bottomRight",
                "frame": "true"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "decTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "decTitle",
            "title": "Decorative components"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "decCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "decCt",
            "layout": "grid1",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "title",
              "text": "commonTitle",
              "cls": "Wb.Title",
              "_expanded": true,
              "properties": {
                "cid": "commonTitle",
                "title": "Title"
              }
            },
            {
              "_icon": "title",
              "text": "iconTitle",
              "cls": "Wb.Title",
              "_expanded": true,
              "properties": {
                "cid": "iconTitle",
                "title": "Icon title",
                "icon": "external-link"
              }
            },
            {
              "_icon": "title",
              "text": "noLineTitle",
              "cls": "Wb.Title",
              "_expanded": true,
              "properties": {
                "cid": "noLineTitle",
                "title": "Title without line",
                "icon": "map",
                "line": "false"
              }
            },
            {
              "_icon": "line",
              "text": "commonLine",
              "cls": "Wb.Line",
              "properties": {
                "cid": "commonLine"
              }
            },
            {
              "_icon": "line",
              "text": "titleLine",
              "cls": "Wb.Line",
              "properties": {
                "cid": "titleLine",
                "title": "Line Title"
              }
            },
            {
              "_icon": "line",
              "text": "dashedLine",
              "cls": "Wb.Line",
              "properties": {
                "cid": "dashedLine",
                "dashed": "true",
                "title": "Dashed Line",
                "dimTitle": "true"
              }
            },
            {
              "_icon": "line",
              "text": "iconLine",
              "cls": "Wb.Line",
              "properties": {
                "cid": "iconLine",
                "icon": "favorite",
                "title": "Icon line"
              }
            },
            {
              "_icon": "fieldset",
              "text": "horizontalFieldSet",
              "cls": "Wb.Fieldset",
              "properties": {
                "cid": "horizontalFieldSet",
                "title": "Horizontal split and fill",
                "icon": "splitter",
                "layout": "row"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "button1",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button1",
                    "text": "Button 1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "_expanded": true,
                  "properties": {
                    "cid": "divider1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button2",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button2",
                    "text": "Button 2"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button3",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button3",
                    "text": "Button 3"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "space",
                  "text": "space1",
                  "cls": "Wb.Space",
                  "_expanded": false,
                  "properties": {
                    "cid": "space1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button4",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button4",
                    "text": "Button 4"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "right1",
                  "text": "fill1",
                  "cls": "Wb.Fill",
                  "properties": {
                    "cid": "fill1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button5",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button5",
                    "text": "Button 5"
                  },
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "fieldset",
              "text": "verticalFieldSet",
              "cls": "Wb.Fieldset",
              "properties": {
                "cid": "verticalFieldSet",
                "title": "Vertical split and fill",
                "icon": "splitter",
                "layout": "column",
                "height": "20em",
                "width": "20em"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "button1",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button1",
                    "text": "Button 1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "_expanded": true,
                  "properties": {
                    "cid": "divider1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button2",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button2",
                    "text": "Button 2"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button3",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button3",
                    "text": "Button 3"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "space",
                  "text": "space1",
                  "cls": "Wb.Space",
                  "_expanded": false,
                  "properties": {
                    "cid": "space1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button4",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button4",
                    "text": "Button 4"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "right1",
                  "text": "fill1",
                  "cls": "Wb.Fill",
                  "properties": {
                    "cid": "fill1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "button",
                  "text": "button5",
                  "cls": "Wb.Button",
                  "_expanded": true,
                  "properties": {
                    "cid": "button5",
                    "text": "Button 5"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "compTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "compTitle",
            "title": "Component"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "compCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "compCt",
            "layout": "form",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "component",
              "text": "component1",
              "cls": "Wb.Component",
              "properties": {
                "cid": "component1",
                "height": "7em",
                "width": "25em",
                "cls": "w-key-color",
                "html": "Component is a \"div\" element that can show HTML or serve as a placeholder for third-party components.",
                "padding": "true",
                "border": "true",
                "autoScroll": "true"
              }
            },
            {
              "_icon": "file-json",
              "text": "script1",
              "cls": "Wb.Script",
              "properties": {
                "cid": "script1",
                "script": "({ cname: 'component', html: '<div>Custom script</div>' })"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: sync-select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let update = [], user_id;\n\nuser_id = Wb.getBool('include') ? 'admin' : 'default';\nWb.getObject('items').forEach(item => {\n  update.push({ $sid: item.sid, user_id });\n});\nWb.sync({ tableName: 'wb_staff', update });"
  },
  "_icon": "module"
}

File name: dual-box.xwl


{
  "title": "",
  "icon": "dual-box",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "DualBox",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "localDataTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "localDataTitle",
            "title": "Local data"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "localDataLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "localDataLabel",
            "text": "Use local data to select"
          }
        },
        {
          "_icon": "dual-box",
          "text": "localDataDualBox",
          "cls": "Wb.DualBox",
          "properties": {
            "cid": "localDataDualBox",
            "sourceData": "[\n  { code: \"10001\", full_name: \"Terri Duffy\" },\n  { code: \"10002\", full_name: \"Roberto Tamburello\" },\n  { code: \"10003\", full_name: \"Michael Sullivan\" },\n  { code: \"10004\", full_name: \"Sharon Salavaria\" },\n  { code: \"10005\", full_name: \"Gail Erickson\" },\n  { code: \"10006\", full_name: \"Jossef Goldberg\" }\n]\n",
            "destData": "[\n  { code: \"10007\", full_name: \"Ovidiu Cracium\" },\n  { code: \"10008\", full_name: \"Janice Galvin\" },\n  { code: \"10009\", full_name: \"Thierry D'Hers\" }\n]",
            "height": "15em",
            "textField": "full_name",
            "subtextField": "code"
          }
        },
        {
          "_icon": "button",
          "text": "getSelectedDataBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "getSelectedDataBtn",
            "text": "Get Selected Data",
            "justifySelf": "start"
          },
          "events": {
            "click": "Wb.tip('Selected: ' + Wb.encode(app.localDataDualBox.destData));"
          }
        },
        {
          "_icon": "title",
          "text": "remoteDataTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "remoteDataTitle",
            "title": "Remote data"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "remoteDataLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "remoteDataLabel",
            "text": "Use remote data to select and sync selections in database"
          }
        },
        {
          "_icon": "dual-box",
          "text": "remoteDataDualBox",
          "cls": "Wb.DualBox",
          "properties": {
            "cid": "remoteDataDualBox",
            "height": "15em",
            "sourceUrl": "demo-source?xaction=selectNoneAdminStaff",
            "destUrl": "demo-source?xaction=selectAdminStaff",
            "autoLoad": "false",
            "searchBar": "true"
          },
          "events": {
            "ready": "let me = this;\n\nme.sourceGrid.pageSize = 10;\nme.sourceGrid.load();\nme.destGrid.pageSize = 10;\nme.destGrid.load();",
            "beforemove": "Wb.ajax({\n  url: xpath + '/sync-select',\n  params: { items, include },\n  success: f => {\n    this.acceptMove(); // this == app.remoteDataDualBox;\n  }\n});\nreturn false;"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "sourceGridConfigs",
                "isProperty": "true",
                "title": "User List"
              },
              "text": "sourceGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            },
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "destGridConfigs",
                "isProperty": "true",
                "title": "Admin Users"
              },
              "text": "destGridConfigs",
              "_expanded": true,
              "_icon": "grid",
              "hasDupCid": 0
            }
          ]
        },
        {
          "_icon": "title",
          "text": "customizeColsTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "customizeColsTitle",
            "title": "Customize Columns"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "customizeColsLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "customizeColsLabel",
            "text": "Customize grid columns"
          }
        },
        {
          "_icon": "dual-box",
          "text": "customizeColsDualBox",
          "cls": "Wb.DualBox",
          "properties": {
            "cid": "customizeColsDualBox",
            "height": "15em",
            "sourceUrl": "demo-source?xaction=staffDict",
            "gridHeader": "true"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "codeCol",
                    "fieldName": "code",
                    "text": "Code"
                  },
                  "text": "codeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fullNameCol",
                    "fieldName": "full_name",
                    "text": "Full name",
                    "width": "-1"
                  },
                  "text": "fullNameCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            },
            {
              "cls": "Wb.Grid",
              "properties": {
                "cid": "sourceGridConfigs",
                "isProperty": "true",
                "pagingBar": "false"
              },
              "text": "sourceGridConfigs",
              "_expanded": true,
              "_icon": "grid"
            }
          ]
        }
      ]
    }
  ]
}

File name: grid.xwl


{
  "title": "",
  "icon": "grid",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "description": "demo-source=example/common/demo-source.xwl"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "cls": "Wb.Menu",
      "properties": {
        "cid": "shareMenu",
        "isProperty": "true",
        "autoDestroy": "false"
      },
      "text": "shareMenu",
      "_expanded": true,
      "_icon": "menu2",
      "hasDupCid": 0,
      "items": [
        {
          "_icon": "item",
          "text": "item1",
          "cls": "Wb.Item",
          "_expanded": true,
          "properties": {
            "cid": "item1",
            "text": "Item1"
          },
          "events": {
            "click": "Wb.tip('Set ' + app.popupRecord.data.full_name);"
          }
        },
        {
          "_icon": "item",
          "text": "item2",
          "cls": "Wb.Item",
          "_expanded": true,
          "properties": {
            "cid": "item2",
            "text": "Item2"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "item",
          "text": "item3",
          "cls": "Wb.Item",
          "_expanded": false,
          "properties": {
            "cid": "item3",
            "text": "Item3"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Grid",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "commonRemoteGridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commonRemoteGridTitle",
            "title": "Common remote grid"
          }
        },
        {
          "_icon": "grid",
          "text": "commonRemoteGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "commonRemoteGrid",
            "height": "15em",
            "url": "demo-source?xaction=staffAllDict",
            "pageSize": "10"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "pagenum",
              "text": "pagingBar",
              "cls": "Wb.PagingBar",
              "_expanded": true,
              "properties": {
                "cid": "pagingBar",
                "isProperty": "true",
                "showPageEdit": "false"
              },
              "hasDupCid": 0
            }
          ]
        },
        {
          "_icon": "title",
          "text": "miscGridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "miscGridTitle",
            "title": "Miscillaneous grid"
          },
          "_expanded": true
        },
        {
          "_icon": "grid",
          "text": "miscGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "miscGrid",
            "height": "20em",
            "localData": "[\n  { _icon: \"user1\", code: \"10001\", full_name: \"Terri Duffy\", gender: \"Female\", birth_date: \"2002-07-01\".dateValue, salary: 28743, checked: 1, email: \"terri0@geejing.com\" },\n  { _icon: \"user\", code: \"10002\", full_name: \"Roberto Tamburello\", gender: \"Male\", birth_date: \"1986-12-13\".dateValue, salary: 16453, checked: 0, email: \"roberto0@geejing.com\" },\n  { _icon: \"user\", code: \"10003\", full_name: \"Michael Sullivan\", gender: \"Male\", birth_date: \"1991-07-17\".dateValue, salary: 32893, checked: 1, email: \"michael8@geejing.com\" },\n  { _icon: \"user1\", code: \"10004\", full_name: \"Sharon Salavaria\", gender: \"Female\", birth_date: \"1973-06-03\".dateValue, salary: 63237, checked: 0, email: \"sharon0@geejing.com\" },\n  { _icon: \"user1\", code: \"10005\", full_name: \"Gail Erickson\", gender: \"Female\", birth_date: \"1964-10-29\".dateValue, salary: 63219.5, checked: 1, email: \"gail0@geejing.com\" },\n  { _icon: \"user\", code: \"10006\", full_name: \"Jossef Goldberg\", gender: \"Male\", birth_date: \"1971-04-11\".dateValue, salary: 23130, checked: 0, email: \"jossef0@geejing.com\" },\n  { _icon: \"user\", code: \"10007\", full_name: \"Ovidiu Cracium\", gender: \"Male\", birth_date: \"1990-02-18\".dateValue, salary: 83231, checked: 0, email: \"ovidiu0@geejing.com\" },\n  { _icon: \"user1\", code: \"10008\", full_name: \"Janice Galvin\", gender: \"Female\", birth_date: \"2001-06-29\".dateValue, salary: 152310, checked: 0, email: \"janice0@geejing.com\" },\n  { _icon: \"user\", code: \"10009\", full_name: \"Thierry D'Hers\", gender: \"Male\", birth_date: \"1971-08-29\".dateValue, salary: 2383, checked: 1, email: \"thierry0@geejing.com\" },\n  { _icon: \"user\", code: \"10010\", subtext: 'Sub text', full_name: \"Brian Welcker\", gender: \"Male\", birth_date: \"1989-07-08\".dateValue, salary: 98313, checked: 1, email: \"brian3@geejing.com\" },\n  { _icon: \"user\", code: \"10011\", full_name: \"Stephen Jiang\", gender: \"Male\", birth_date: \"1963-11-17\".dateValue, salary: 53231, checked: 0, email: \"stephen0@geejing.com\" },\n  { _icon: \"user\", code: \"10012\", full_name: \"Syed Abbas\", gender: \"Male\", birth_date: \"1987-02-11\".dateValue, salary: 38925.8, checked: 1, _disabled: true, email: \"syed0@geejing.com\" }\n]",
            "pageSize": "10",
            "showIcon": "true",
            "sorters": "full_name",
            "columnsSortable": "true",
            "resizable": "true",
            "stateId": "wb.miscGrid",
            "frame": "true",
            "captureClass": "@'.w-btn' /* prevent select and focus row when click row buttons */",
            "exportFilename": "staff-list",
            "exportTitle": "Staff List"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "Wb.tip(item.data.full_name + ' row double clicked');",
            "click": "let link = e.target.closest('.my-link');\nif (link) {\n  let row = this.findItemByEl(link);\n  Wb.tip(link.textContent + ' clicked at ' + row.data.full_name);\n}",
            "beforesort": "// Customize local sort\n// data.sort((a, b) => Wb.mixCompare(a.full_name, b.full_name));\n// return false;"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true",
                    "freeze": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "codeCol",
                    "text": "Code",
                    "width": "8em",
                    "fieldName": "code",
                    "freeze": "true",
                    "summaryRender": "return 'Total:';"
                  },
                  "text": "codeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "personalCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "personalCol",
                    "text": "Personal"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "_icon": "column",
                      "text": "commonCol",
                      "cls": "Wb.Column",
                      "_expanded": true,
                      "properties": {
                        "cid": "commonCol",
                        "text": "Common"
                      },
                      "items": [
                        {
                          "cls": "Wb.Column",
                          "properties": {
                            "cid": "fullNameCol",
                            "width": "-1",
                            "fieldName": "full_name",
                            "autoWrap": "normal",
                            "titleAlign": "center",
                            "titleAutoWrap": "normal",
                            "html": "<span class='w-error-color'>Full name</span>",
                            "align": "center",
                            "summary": "count",
                            "summaryRender": "return value + ' P';",
                            "render": "el.setStyle('background', 'linear-gradient(to right, rgb(142 36 159) ' + (data.salary / 1800) + '%, rgb(85, 85, 85) 0%)', 'important');\nel.setStyle('color', '#fff');\nreturn value;"
                          },
                          "text": "fullNameCol",
                          "_expanded": true,
                          "_icon": "column"
                        },
                        {
                          "cls": "Wb.Column",
                          "properties": {
                            "cid": "genderCol",
                            "text": "Gender",
                            "width": "8em",
                            "fieldName": "gender",
                            "render": "let div = el.addEl('w-center').addEl('w-ta-center');\n\ndiv.style = `color:#fff;width:5em;background:${value == 'Male' ? '#ff5d8d' : '#095c09'}`;\ndiv.textContent = value;"
                          },
                          "text": "genderCol",
                          "_expanded": true,
                          "_icon": "column"
                        }
                      ]
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "birthDateCol",
                        "text": "Birth_date",
                        "width": "12em",
                        "fieldName": "birth_date",
                        "render": "if (value < '1974-1-1'.dateValue) {\n  // if (value < '1974-1-1'.dateValue) {\n  //   let div = el.parentNode;\n\n  //   //tip: Setting important classes or styles for elements has higher css priority and can override the default background/color\n  //   //Setting td classes/styles will override all default background/color\n  //   if (value < '1968-1-1'.dateValue) {\n  //     div.setStyle('background', '#1890ff');\n  //   } else {\n  //     div.setStyle('background', '#f90');\n  //   }\n  //   div.setStyle('color', 'white');\n  //   value = value + ' *';\n  // }\n  //w-succ-bgcolor-row, w-error-bgcolor-row etc\n  el.parentNode.cls = value < '1968-1-1'.dateValue ? 'w-succ-bgcolor-row' : 'w-warn-bgcolor-row';\n  value += ' *';\n};\n\n//add icon manually\nel.insertCellIcon('time', value);\n//el.insertCellImage('wb/images/sound.png');"
                      },
                      "text": "birthDateCol",
                      "_expanded": true,
                      "_icon": "column"
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "salaryCol",
                    "text": "Salary",
                    "fieldName": "salary",
                    "titleAlign": "right",
                    "type": "usd",
                    "summary": "sum",
                    "summaryRender": "let ct = data.full_name;// full_name column summary is \"count\"\nif (ct == null)\n  return ''; // full_name column is hidden\nelse\n  return 'Avg: ' + column.formatValue(value / ct);",
                    "align": "right"
                  },
                  "text": "salaryCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "checkCol1",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol1",
                    "checkBox": "int",
                    "fieldName": "checked",
                    "checkRefresh": "true",
                    "menuText": "Check"
                  }
                },
                {
                  "_icon": "column",
                  "text": "checkCol2",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol2",
                    "checkBox": "roTrue",
                    "fieldName": "checked"
                  }
                },
                {
                  "_icon": "column",
                  "text": "checkCol3",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol3",
                    "checkBox": "roAll",
                    "fieldName": "checked"
                  }
                },
                {
                  "_icon": "column",
                  "text": "toggleCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "toggleCol",
                    "render": "let row = this;\nvalue = row.data.checked;\nrow.addFooter({\n  cname: 'toggle', returnType: 'int', value, toggleAlign: 'center', clickToggle: true,\n  events: {\n    change(value) {\n      // If there is no need to synchronously update other fields, this is more efficient\n      // row.data.checked = value; // only update checked field value\n      row.set('checked', value); // update whole row\n      app.checkCol1.syncHeaderCheck(); // manual sync header check of checkCol1\n    }\n  }\n}, el);",
                    "width": "5em",
                    "text": "Toggle"
                  }
                },
                {
                  "_icon": "column",
                  "text": "linkCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "linkCol",
                    "render": "let a = el.addEl('my-link', 'a');\nel.cls = 'w-ta-center';\na.textContent = 'Download';\n// a.onclick = f => Wb.tip(data.full_name); // not recommended\n// miscGrid.click mon the link click event  // recommended",
                    "width": "8em",
                    "text": "Link"
                  }
                },
                {
                  "_icon": "column",
                  "text": "actionsCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "actionsCol",
                    "text": "Actions",
                    "width": "8em",
                    "freeze": "true"
                  },
                  "items": [
                    {
                      "cls": "Wb.Array",
                      "properties": {
                        "cid": "buttons"
                      },
                      "text": "buttons",
                      "_expanded": true,
                      "_icon": "array",
                      "items": [
                        {
                          "_icon": "item",
                          "text": "setItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "setItem",
                            "icon": "gear",
                            "tip": "Set"
                          },
                          "events": {
                            "click": "Wb.tip('Set ' + this.data.code + ', ' + this.gridItem.data.code);"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "shareItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "shareItem",
                            "icon": "share2",
                            "tip": "Add"
                          },
                          "events": {
                            "click": "Wb.tip('Share ' + this.data.code);"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "menuItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "menuItem",
                            "icon": "menu"
                          },
                          "events": {
                            "click": "app.popupRecord = this.parent.parent;\n//app.popupRecord = app.miscGrid.selection; // same as above\n//app.popupRecord = this.el.getClosestComp(Wb.ViewItem); // same as above\napp.shareMenu.showBy(this);"
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "cls": "Wb.Menu",
              "properties": {
                "cid": "contextMenu",
                "isProperty": "true"
              },
              "text": "contextMenu",
              "_expanded": true,
              "_icon": "menu2",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "showRowColumnItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "showRowColumnItem",
                    "icon": "grid",
                    "text": "Show row column"
                  },
                  "events": {
                    "click": "let el, row, col;\n\nel = this.contextTarget; // clicked element\nrow = app.miscGrid.findItemByEl(el); // same as app.miscGrid.selection\ncol = app.miscGrid.findColumnByEl(el);\nif (row && col)\n  Wb.tip('Row code is ' + row.data.code + ' and column field name is ' + col.fieldName);"
                  }
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "text": "Other item"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "form",
            "padding": "false"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "testfrozenBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "testfrozenBtn",
                "text": "Test frozen columns"
              },
              "events": {
                "click": "app.fullNameCol.width = app.miscGrid.width;\nWb.toast('Now please scroll horizontally to test the left and right frozen columns.')"
              }
            },
            {
              "_icon": "button",
              "text": "sortDataBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "sortDataBtn",
                "text": "Sort Data"
              },
              "events": {
                "click": "app.miscGrid.sort({ name: 'code', desc: true });"
              }
            },
            {
              "_icon": "button",
              "text": "sortItemsBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "sortItemsBtn",
                "text": "Sort Items"
              },
              "events": {
                "click": "// Sort view items only\napp.miscGrid.sortItems((a, b) => Wb.mixCompare(a.data.code, b.data.code));"
              }
            }
          ]
        },
        {
          "_icon": "component",
          "text": "miscDescComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "miscDescComp",
            "html": "<div>Feature List:</div>\n<ul>\n  <li>Columns sortable, resizable, draggable and freezable.</li>\n  <li>Arbitrary multi-Level Headers.</li>\n  <li>Customize cell rendering.</li>\n  <li>Rows and columns highlighing.</li>\n  <li>Local data pagination.</li>\n  <li>Bind buttons or any controls to cells.</li>\n  <li>Multiple styles of check columns.</li>\n  <li>Customize columns and persistence to the database.</li>\n  <li>Summary row.</li>\n</ul>",
            "textSelectable": "false"
          }
        },
        {
          "_icon": "title",
          "text": "editableGridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "editableGridTitle",
            "title": "Editable grid"
          }
        },
        {
          "_icon": "grid",
          "text": "editableGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "editableGrid",
            "height": "15em",
            "url": "demo-source?xaction=staffAllDict",
            "columnsSortable": "true",
            "sorters": "[{name: 'gender', desc: true}, 'salary']",
            "editable": "true",
            "multiSelect": "true",
            "pageSize": "10",
            "arrowDownAppend": "true",
            "enterAppend": "true",
            "enterAsTab": "true",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "//Some related methods: grid.add()/grid.addData()/grid.insert()/grid.insertData()/grid.insertXXX()/grid.insertDataXXX() etc.\napp.editableGrid.addRecord();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "editBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "editBtn",
                    "text": "@Str.edit",
                    "icon": "edit",
                    "keys": "Ctrl+J"
                  },
                  "events": {
                    "click": "let firstRec = app.editableGrid.firstItem;\nif (firstRec) {\n  firstRec.set({ code: 'code', full_name: 'new name' });\n  app.editableGrid.startEdit(firstRec, 'code');\n  firstRec.highlight();\n}"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "//Some related methods: grid.destroySelection()/item.destroy()/Wb.destroy(items);\napp.editableGrid.delRecords();"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "item",
                  "text": "saveBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "saveBtn",
                    "text": "@Str.save",
                    "icon": "save",
                    "keys": "Ctrl+S"
                  },
                  "events": {
                    "click": "app.editableGrid.sync({\n  url: xpath, //Should be changed to a valid URL\n  success() {\n    Wb.tipDone(Str.save);\n  }\n});"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "dbPaginationTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dbPaginationTitle",
            "title": "Database side pagination"
          }
        },
        {
          "_icon": "label",
          "text": "dbPaginationLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "dbPaginationLabel",
            "text": "This example demonstrates database side pagination."
          }
        },
        {
          "_icon": "grid",
          "text": "dbPaginationGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "dbPaginationGrid",
            "height": "15em",
            "url": "demo-source?xaction=staffDbPaging",
            "pageSize": "10",
            "pagingBar": "pageNums"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "noPagingTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "noPagingTitle",
            "title": "No pagination"
          }
        },
        {
          "_icon": "grid",
          "text": "noPagingGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "noPagingGrid",
            "height": "15em",
            "url": "demo-source?xaction=staffAllDict",
            "pageSize": "-1"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "mergeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "mergeTitle",
            "title": "Merge rows and columns"
          }
        },
        {
          "_icon": "label",
          "text": "mergeLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "mergeLabel",
            "text": "Merge f1 rows and merge f1, f2 columns, see f1Col and f2Col properties."
          }
        },
        {
          "_icon": "grid",
          "text": "mergeGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "mergeGrid",
            "url": "demo-source?xaction=selectReport1"
          },
          "_expanded": false,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f1Col",
                    "text": "f1",
                    "fieldName": "f1",
                    "mergeRows": "true",
                    "mergeColsGroup": "mergeTotal",
                    "autoWrap": "normal",
                    "width": "12em"
                  },
                  "text": "f1Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f2Col",
                    "text": "f2",
                    "fieldName": "f2",
                    "mergeColsGroup": "mergeTotal",
                    "width": "14em"
                  },
                  "text": "f2Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f3Col",
                    "text": "f3",
                    "fieldName": "f3"
                  },
                  "text": "f3Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f4Col",
                    "text": "f4",
                    "fieldName": "f4"
                  },
                  "text": "f4Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f5Col",
                    "text": "f5",
                    "fieldName": "f5"
                  },
                  "text": "f5Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f6Col",
                    "text": "f6",
                    "fieldName": "f6"
                  },
                  "text": "f6Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f7Col",
                    "text": "f7",
                    "fieldName": "f7"
                  },
                  "text": "f7Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f8Col",
                    "text": "f8",
                    "fieldName": "f8"
                  },
                  "text": "f8Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f9Col",
                    "text": "f9",
                    "fieldName": "f9"
                  },
                  "text": "f9Col",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "f10Col",
                    "text": "f10",
                    "fieldName": "f10"
                  },
                  "text": "f10Col",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "dragDropTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dragDropTitle",
            "title": "Drag-drop rows"
          }
        },
        {
          "_icon": "grid",
          "text": "dragDropGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "dragDropGrid",
            "height": "30em",
            "url": "demo-source?xaction=staffAllDict",
            "pageSize": "10",
            "draggable": "true",
            "droppable": "demo.dragDropGrid",
            "multiSelect": "true"
          },
          "_expanded": true,
          "events": {
            "itemdrop": "let n = draggable.source.length;\nWb.tip(draggable.source.length + ' ' + (n > 1 ? 'items' : 'item') + ' dropped.');",
            "itemdrag": "if (draggable.source.some(row => row.data.code == '10009') || draggable.dest.data.code == '10001') {\n  //Do not allow to drop when the source record code contains 10009 or dest record code is 10001\n  draggable.allowDrop = false;\n}"
          }
        },
        {
          "_icon": "title",
          "text": "propertyGridTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "propertyGridTitle",
            "title": "Property grid"
          },
          "_expanded": true
        },
        {
          "_icon": "grid",
          "text": "propertyGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "propertyGrid",
            "localData": "[\n  { name: 'String', value: 'abc' },\n  { name: 'Integer', value: 123 },\n  { name: 'Float', value: 456.78 },\n  { name: 'Date', value: new Date().datePart },\n  { name: 'Boolean', value: true },\n]",
            "frame": "true",
            "editable": "true",
            "pagingBar": "false"
          },
          "_expanded": true,
          "events": {
            "beforeedit": "if (column.fieldName == 'value') {\n  app.editors ??= {\n    String: new Wb.Text(),\n    Integer: new Wb.Number({ decimalCount: 0 }),\n    Float: new Wb.Number(),\n    Date: new Wb.Date(),\n    Boolean: new Wb.BoolSelect()\n  };\n  column.editor = app.editors[row.data.name] ?? app.editors.String;\n}"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "text": "Name",
                    "fieldName": "name",
                    "render": "el.addCls('w-fixed-cell');\nreturn value;"
                  },
                  "text": "nameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "valueCol",
                    "text": "Value",
                    "width": "-1",
                    "fieldName": "value",
                    "align": "left"
                  },
                  "text": "valueCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: html-editor.xwl


{
  "title": "",
  "icon": "file-html",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "file-html",
          "text": "htmlEditor1",
          "cls": "Wb.HtmlEditor",
          "properties": {
            "cid": "htmlEditor1",
            "wrapBorder": "false",
            "value": "<p>\n  <strong class=\"ql-font-Arial ql-size-p16\" style=\"color: fuchsia;\">Rich</strong>\n  <u class=\"ql-font-Impact ql-size-p28\" style=\"color: lime;\">Text</u>\n  <em class=\"ql-size-p36 ql-font-Times-New-Roman\" style=\"color: blue;\">Editor</em>\n</p>"
          }
        }
      ]
    }
  ]
}

File name: input.xwl


{
  "title": "",
  "icon": "text",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Input controls",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "textTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "textTitle",
            "title": "Text"
          }
        },
        {
          "_icon": "container",
          "text": "textCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "textCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "commonText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "commonText",
                "text": "Common",
                "placeholder": "placeholder"
              }
            },
            {
              "_icon": "text",
              "text": "passwordText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "passwordText",
                "text": "Password",
                "eyeButton": "true",
                "password": "true",
                "icon": "lock"
              }
            },
            {
              "_icon": "text",
              "text": "clearText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "clearText",
                "text": "With Clear",
                "clearButton": "true"
              }
            },
            {
              "_icon": "text",
              "text": "hintText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "hintText",
                "hint": "Please enter your name",
                "text": "Input Hint"
              }
            },
            {
              "_icon": "text",
              "text": "alignText",
              "cls": "Wb.Text",
              "_expanded": false,
              "properties": {
                "cid": "alignText",
                "text": "Left Align Text",
                "labelAlign": "left"
              }
            },
            {
              "_icon": "text",
              "text": "prefixSuffixText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "prefixSuffixText",
                "prefix": "$",
                "suffix": "%",
                "text": "Prefix Suffix"
              }
            },
            {
              "_icon": "text",
              "text": "readonlyText",
              "cls": "Wb.Text",
              "_expanded": false,
              "properties": {
                "cid": "readonlyText",
                "readonly": "true",
                "text": "Readonly"
              }
            },
            {
              "_icon": "text",
              "text": "disabledText",
              "cls": "Wb.Text",
              "_expanded": false,
              "properties": {
                "cid": "disabledText",
                "disabled": "true",
                "text": "Disabled"
              }
            },
            {
              "_icon": "text",
              "text": "emptyLabelText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "emptyLabelText",
                "showEmptyLabel": "true",
                "placeholder": "empty label"
              }
            },
            {
              "_icon": "text",
              "text": "loadingText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "loadingText",
                "spin": "true",
                "icon": "loading1",
                "text": "Loading"
              }
            },
            {
              "_icon": "text",
              "text": "flatText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "flatText",
                "text": "Flat",
                "flat": "true",
                "inactiveBorder": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "label1",
              "text": "displayField1",
              "cls": "Wb.DisplayField",
              "properties": {
                "cid": "displayField1",
                "value": "Display value",
                "icon": "info",
                "showEmptyLabel": "true"
              }
            },
            {
              "_icon": "label1",
              "text": "displayField2",
              "cls": "Wb.DisplayField",
              "properties": {
                "cid": "displayField2",
                "value": "No label",
                "icon": "info"
              }
            },
            {
              "_icon": "text",
              "text": "iconLabelText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "iconLabelText",
                "labelIcon": "book1",
                "text": "Icon label",
                "labelAlign": "left",
                "labelSeparator": "false"
              }
            },
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "_expanded": true,
              "properties": {
                "cid": "fill1"
              }
            },
            {
              "_icon": "text",
              "text": "upLabelText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "upLabelText",
                "labelUp": "true",
                "text": "The label is above the input box"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "numberTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "numberTitle",
            "title": "Number"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "numberCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "numberCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "number-edit",
              "text": "commonNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "commonNumber",
                "text": "Common Number"
              }
            },
            {
              "_icon": "number-edit",
              "text": "upDownNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "upDownNumber",
                "text": "Up Down",
                "spinner": "upDown"
              },
              "_expanded": true
            },
            {
              "_icon": "number-edit",
              "text": "leftRightNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "leftRightNumber",
                "text": "Left Right",
                "spinner": "leftRight"
              }
            },
            {
              "_icon": "number-edit",
              "text": "beforeAfterNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "beforeAfterNumber",
                "text": "Before After",
                "spinner": "beforeAfter"
              }
            },
            {
              "_icon": "number-edit",
              "text": "noSpinnerNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "noSpinnerNumber",
                "text": "No Spinner",
                "spinner": "false",
                "numberInput": "true /* Show number keyboard in phone */"
              }
            },
            {
              "_icon": "number-edit",
              "text": "step5Number",
              "cls": "Wb.Number",
              "properties": {
                "cid": "step5Number",
                "text": "Step 5",
                "step": "5"
              }
            },
            {
              "_icon": "number-edit",
              "text": "dicimal3Number",
              "cls": "Wb.Number",
              "properties": {
                "cid": "dicimal3Number",
                "text": "Max 3 Dicimals",
                "decimalCount": "3"
              },
              "_expanded": true
            },
            {
              "_icon": "number-edit",
              "text": "autoFormatNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "autoFormatNumber",
                "text": "Auto Format",
                "decimalCount": "2",
                "prefix": "$",
                "suffix": "%",
                "autoFormat": "true",
                "value": "1234.567"
              },
              "_expanded": true
            },
            {
              "_icon": "number-edit",
              "text": "constrainNumber",
              "cls": "Wb.Number",
              "properties": {
                "cid": "constrainNumber",
                "text": "10 - 20",
                "minValue": "10",
                "maxValue": "20"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "textAreaTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "textAreaTitle",
            "title": "Text Area"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "textAreaCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "textAreaCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "textarea",
              "text": "commonTextArea",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "commonTextArea",
                "text": "Common TextArea"
              },
              "_expanded": true
            },
            {
              "_icon": "textarea",
              "text": "noWrapTextArea",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "noWrapTextArea",
                "text": "No Wrap",
                "value": "WebBuilder is a powerful rapid application development and runtime platform",
                "noWrap": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "textarea",
              "text": "overflowTextArea",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "overflowTextArea",
                "text": "Now The Text Label Overflowed"
              },
              "_expanded": true
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line1"
              }
            },
            {
              "_icon": "textarea",
              "text": "autoResizeTextArea",
              "cls": "Wb.TextArea",
              "properties": {
                "cid": "autoResizeTextArea",
                "text": "Auto Resize",
                "autoResize": "true",
                "rows": "1",
                "value": "Auto resize to fit content height",
                "maxHeight": "10em"
              },
              "_expanded": true
            }
          ]
        },
        {
          "_icon": "title",
          "text": "fileInputTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "fileInputTitle",
            "title": "File Input"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "fileInputLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "fileInputLabel",
            "text": "You can drag and drop files from the operating system shell to the file control."
          }
        },
        {
          "_icon": "container",
          "text": "fileInputCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "fileInputCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "search-file",
              "text": "commonFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "commonFileInput",
                "text": "Common"
              }
            },
            {
              "_icon": "search-file",
              "text": "multipleFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "multipleFileInput",
                "text": "Multiple",
                "multiple": "true"
              }
            },
            {
              "_icon": "search-file",
              "text": "browseFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "browseFileInput",
                "browseMode": "true",
                "text": "Browse file",
                "multiple": "true"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "validationTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "validationTitle",
            "title": "Validation value"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "validationCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "validationCt",
            "layout": "grid",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "requiredText1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "requiredText1",
                "text": "Required1",
                "required": "true"
              }
            },
            {
              "_icon": "text",
              "text": "requiredText2",
              "cls": "Wb.Text",
              "properties": {
                "cid": "requiredText2",
                "text": "Required2",
                "required": "true",
                "sideErrorTip": "true"
              }
            },
            {
              "_icon": "text",
              "text": "lengthText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "lengthText",
                "text": "Limit Length",
                "minLength": "3",
                "maxLength": "9"
              }
            },
            {
              "_icon": "text",
              "text": "alphaNumText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "alphaNumText",
                "valueType": "alphaNum",
                "text": "Alpha num"
              }
            },
            {
              "_icon": "text",
              "text": "emailText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "emailText",
                "valueType": "email",
                "text": "Email"
              }
            },
            {
              "_icon": "text",
              "text": "urlText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "urlText",
                "valueType": "url",
                "text": "URL"
              }
            },
            {
              "_icon": "text",
              "text": "regExpText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "regExpText",
                "text": "Regular Exp",
                "regexText": "Please enter at least 5 digits",
                "placeholder": "enter 5 digits",
                "regex": "/\\d{5}$/"
              }
            },
            {
              "_icon": "text",
              "text": "customValidateText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "customValidateText",
                "validator": "if (value != 'good')\n  return 'Please enter \"good\"';",
                "text": "Custom Validation"
              }
            },
            {
              "_icon": "text",
              "text": "remoteValidateText",
              "cls": "Wb.Text",
              "_expanded": true,
              "properties": {
                "cid": "remoteValidateText",
                "validator": "let editor = this;\n\nWb.ajax({\n  url: 'demo-source?xaction=checkUser',\n  params: { username: value },\n  mask: false,\n  callback(resp, xhr) {\n    editor.removeError();\n    if (resp == 'failed' || !xhr.ok)\n      editor.addError(Str.alreadyExists.format(value));\n  }\n});",
                "text": "Remote Validation",
                "placeholder": "user name"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "sizeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "sizeTitle",
            "title": "Input size"
          }
        },
        {
          "_icon": "label",
          "text": "sizeLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sizeLabel",
            "text": "Set the \"fontSize\" property to set input size"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "sizeCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "sizeCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "text",
                "fontSize": ".8em"
              }
            },
            {
              "_icon": "number-edit",
              "text": "number1",
              "cls": "Wb.Number",
              "properties": {
                "cid": "number1",
                "text": "number",
                "fontSize": "1.2em"
              }
            },
            {
              "_icon": "combo",
              "text": "select1",
              "cls": "Wb.Select",
              "properties": {
                "cid": "select1",
                "text": "select",
                "fontSize": "1.4em",
                "data": "['item1', 'item2', 'item3']"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: misc-comps.xwl


{
  "title": "",
  "icon": "list1",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Miscellaneous comps",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "controlCtTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "controlCtTitle",
            "title": "ControlCt"
          }
        },
        {
          "_icon": "label",
          "text": "controlCtLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "controlCtLabel",
            "text": "Control container, like a control with a label, can also add child controls."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "controlCtCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "controlCtCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "model",
              "text": "controlCt",
              "cls": "Wb.ControlCt",
              "properties": {
                "cid": "controlCt",
                "text": "Container label",
                "layout": "form"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "label",
                  "text": "label1",
                  "cls": "Wb.Label",
                  "properties": {
                    "cid": "label1",
                    "text": "Label"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button1",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button1",
                    "text": "Button"
                  },
                  "_expanded": true
                }
              ]
            },
            {
              "_icon": "text",
              "text": "controlCtAlignText",
              "cls": "Wb.Text",
              "properties": {
                "cid": "controlCtAlignText",
                "text": "Other control"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "scrolledCtTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "scrolledCtTitle",
            "title": "ScrolledCt"
          }
        },
        {
          "_icon": "label",
          "text": "scrolledCtLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "scrolledCtLabel",
            "text": "Scrolled container can automatically scroll without scrollbar."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "scrolledCtCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "scrolledCtCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "position",
              "text": "scrolledCt",
              "cls": "Wb.ScrolledCt",
              "properties": {
                "cid": "scrolledCt",
                "layout": "row",
                "width": "30em",
                "gap": "true",
                "padding": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "button",
                  "text": "button1",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button1",
                    "text": "Button1"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button2",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button2",
                    "text": "Button2"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button3",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button3",
                    "text": "Button3"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button4",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button4",
                    "text": "Button4"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button5",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button5",
                    "text": "Button5"
                  }
                },
                {
                  "_icon": "button",
                  "text": "button6",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "button6",
                    "text": "Button6"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "carouselTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "carouselTitle",
            "title": "Carousel"
          }
        },
        {
          "_icon": "label",
          "text": "carouselLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "carouselLabel",
            "text": "Automatic carousel play container."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "carouselCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "carouselCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "play1",
              "text": "carousel1",
              "cls": "Wb.Carousel",
              "properties": {
                "cid": "carousel1",
                "frame": "true",
                "height": "8em",
                "interval": "2000"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "component",
                  "text": "component1",
                  "cls": "Wb.Component",
                  "properties": {
                    "cid": "component1",
                    "background": "#f88",
                    "cls": "w-center",
                    "text": "Comp1",
                    "style": "color:#fff"
                  }
                },
                {
                  "_icon": "component",
                  "text": "component2",
                  "cls": "Wb.Component",
                  "properties": {
                    "cid": "component2",
                    "background": "#8f8",
                    "cls": "w-center",
                    "text": "Comp2",
                    "style": "color:#000"
                  }
                },
                {
                  "_icon": "component",
                  "text": "component3",
                  "cls": "Wb.Component",
                  "properties": {
                    "cid": "component3",
                    "background": "#ababf7",
                    "cls": "w-center",
                    "text": "Comp3",
                    "style": "color:#fff"
                  }
                },
                {
                  "_icon": "component",
                  "text": "component4",
                  "cls": "Wb.Component",
                  "properties": {
                    "cid": "component4",
                    "background": "#3bdbd9",
                    "cls": "w-center",
                    "text": "Comp4",
                    "style": "color:#fff"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "progressBarTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "progressBarTitle",
            "title": "ProgressBar"
          }
        },
        {
          "_icon": "label",
          "text": "progressBarLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "progressBarLabel",
            "text": "Display progress through graphics and text."
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "progressBarCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "progressBarCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "progress",
              "text": "progressBar1",
              "cls": "Wb.ProgressBar",
              "properties": {
                "cid": "progressBar1",
                "value": "35"
              }
            },
            {
              "_icon": "progress",
              "text": "progressBar2",
              "cls": "Wb.ProgressBar",
              "properties": {
                "cid": "progressBar2",
                "value": "68.5",
                "progressText": "Copying files"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: option.xwl


{
  "title": "",
  "icon": "options",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "autoScroll": "true",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Option",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "commonTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commonTitle",
            "title": "Common Options"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "optionCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "optionCt",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "options",
              "text": "commonOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "commonOption",
                "text": "Common options, click show dialog",
                "border": "bottom",
                "valueText": "Value text"
              },
              "_expanded": true,
              "events": {
                "click": "app.dialog1 ??= new Wb.Dialog({\n  title: 'New dialog', border: 0, defaults: { cname: 'option' }, items: [\n    { cname: 'label', text: 'Group1', indent: true, titleType: 'title5' },\n    { text: 'Option1', handler() { Wb.tip('Option1 clicked') } },\n    { text: 'Option2', handler() { Wb.tip('Option2 clicked') } },\n    '-',\n    { cname: 'label', text: 'Group2', indent: true, titleType: 'title5' },\n    { text: 'Option3', editor: { cname: 'toggle' } },\n    { text: 'Option4', editor: { cname: 'toggle' } },\n  ]\n});\napp.dialog1.show();"
              }
            },
            {
              "_icon": "options",
              "text": "checkedItemsOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "checkedItemsOption",
                "text": "Web browser",
                "border": "bottom",
                "description": "The default browser to be used",
                "options": "[\n  { text: 'Chrome', value: 1, _icon: 'chrome' },\n  { text: 'Edge', value: 2, _icon: 'edge', _selected: true },\n  { text: 'Firefox', value: 3, _icon: 'firefox' },\n  { text: 'Safari', value: 4, _icon: 'safari' }\n]",
                "value": "2",
                "optionsTitle": "Select Browser"
              },
              "_expanded": true
            },
            {
              "_icon": "options",
              "text": "toggleOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "toggleOption",
                "text": "Option with toggle",
                "border": "bottom"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Toggle",
                  "properties": {
                    "cid": "editor",
                    "isProperty": "true"
                  },
                  "text": "editor",
                  "_expanded": true,
                  "_icon": "switcher-on"
                }
              ]
            },
            {
              "_icon": "options",
              "text": "toggleClickOnToggleOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "toggleClickOnToggleOption",
                "text": "Option with toggle, only allow click on the toggle component",
                "border": "bottom",
                "allowClick": "false"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Toggle",
                  "properties": {
                    "cid": "editor",
                    "isProperty": "true"
                  },
                  "text": "editor",
                  "_expanded": true,
                  "_icon": "switcher-on"
                }
              ]
            },
            {
              "_icon": "options",
              "text": "iconOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "iconOption",
                "text": "Option with icon",
                "border": "bottom",
                "icon": "calendar",
                "flagIcon": "menu"
              },
              "_expanded": true,
              "events": {
                "click": "Wb.tip('clicked');"
              }
            },
            {
              "_icon": "options",
              "text": "customOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "customOption",
                "text": "Custom option",
                "border": "bottom",
                "icon": "message",
                "flagIcon": "share1",
                "valueText": "Some value"
              },
              "_expanded": true,
              "events": {
                "click": "Wb.tip('clicked');",
                "ready": "let imgEl = this.el.insertElBefore('w-unselect', 'img', this.flagEl);\nimgEl.src = 'wb/images/order.png';\nimgEl.draggable = false;"
              }
            },
            {
              "_icon": "options",
              "text": "disabledOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "disabledOption",
                "text": "Disabled option",
                "disabled": "true"
              },
              "_expanded": true
            }
          ]
        },
        {
          "_icon": "title",
          "text": "editorTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "editorTitle",
            "title": "Editor Options"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "editorCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "editorCt",
            "background": "true",
            "layout": "grid1"
          },
          "items": [
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line1",
                "title": "Option style"
              }
            },
            {
              "_icon": "container",
              "text": "optionCt1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "optionCt1",
                "background": "false",
                "roundBorder": "true",
                "defaults": "({ labelWidth: '8em' })"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "options",
                  "text": "nameOption",
                  "cls": "Wb.Option",
                  "properties": {
                    "cid": "nameOption",
                    "text": "Name",
                    "border": "bottom"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "flex": "1",
                        "flat": "true",
                        "placeholder": "Your name"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text"
                    }
                  ]
                },
                {
                  "_icon": "options",
                  "text": "heightOption",
                  "cls": "Wb.Option",
                  "properties": {
                    "cid": "heightOption",
                    "text": "Height (cm)",
                    "border": "bottom"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "flex": "1",
                        "flat": "true",
                        "placeholder": "Your height"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "number-edit"
                    }
                  ]
                },
                {
                  "_icon": "options",
                  "text": "genderOption",
                  "cls": "Wb.Option",
                  "properties": {
                    "cid": "genderOption",
                    "text": "Gender",
                    "border": "bottom",
                    "options": "['Male', 'Female']",
                    "optionsTitle": "Select Gender"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "options",
                  "text": "statusOption",
                  "cls": "Wb.Option",
                  "properties": {
                    "cid": "statusOption",
                    "text": "Status"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "cls": "Wb.Toggle",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "switcher-on"
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "line",
              "text": "line2",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line2",
                "title": "Form style"
              }
            },
            {
              "_icon": "container",
              "text": "optionCt2",
              "cls": "Wb.Container",
              "properties": {
                "cid": "optionCt2",
                "background": "false",
                "roundBorder": "true",
                "tableStyle": "simple"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "defaults",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "defaults",
                    "labelWidth": "8em",
                    "labelSeparator": "false",
                    "isProperty": "true"
                  }
                },
                {
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "name",
                    "text": "Name",
                    "placeholder": "Your name"
                  },
                  "text": "name",
                  "_expanded": true,
                  "_icon": "text"
                },
                {
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "height",
                    "text": "Height (cm)",
                    "placeholder": "Your height"
                  },
                  "text": "height",
                  "_expanded": true,
                  "_icon": "number-edit"
                },
                {
                  "_icon": "combo",
                  "text": "gender",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "gender",
                    "data": "(['Male', 'Female'])",
                    "text": "Gender",
                    "placeholder": "Male / Female"
                  }
                },
                {
                  "_icon": "options",
                  "text": "genderOption",
                  "cls": "Wb.Option",
                  "properties": {
                    "cid": "genderOption",
                    "text": "Gender Option",
                    "border": "bottom",
                    "options": "['Male', 'Female']",
                    "optionsTitle": "Select Gender"
                  },
                  "_expanded": true
                },
                {
                  "cls": "Wb.Toggle",
                  "properties": {
                    "cid": "status",
                    "text": "Status"
                  },
                  "text": "status",
                  "_expanded": true,
                  "_icon": "switcher-on"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: select.xwl


{
  "title": "",
  "icon": "combo",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Add trigger button to fetch value. @priv\n   * @param {Wb.Select} select select control.\n   */\n  addFetchValueButton(select) {\n    select.addTrigger({\n      icon: 'share', tip: 'Show value', weight: 991, handler() {\n        Wb.tip(select.cid + ' value is: ' + select.value);\n      }\n    });\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Select, Trigger and Picker",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "selectTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "selectTitle",
            "title": "Select"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "selectCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "selectCt",
            "layout": "grid",
            "frame": "true",
            "gridTplColumns": "repeat(auto-fill, 22em)"
          },
          "items": [
            {
              "_icon": "combo",
              "text": "commonSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "commonSelect",
                "text": "Common Select",
                "data": "['item1', 'item2', 'item3']"
              }
            },
            {
              "_icon": "combo",
              "text": "noEditableSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "noEditableSelect",
                "text": "No Editable",
                "data": "['item1', 'item2', 'item3']",
                "editable": "false"
              }
            },
            {
              "_icon": "combo",
              "text": "nameValueSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "nameValueSelect",
                "text": "Name Value",
                "data": "[['name1', 'value1'], ['name2', 'value2'], ['name3', 'value3']]"
              },
              "events": {
                "ready": "app.addFetchValueButton(this);"
              }
            },
            {
              "_icon": "combo",
              "text": "remoteSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "remoteSelect",
                "text": "Remote Select No Wrap",
                "url": "demo-source?xaction=staffRows",
                "textField": "full_name",
                "valueField": "code",
                "loadOnce": "true",
                "noWrap": "true"
              },
              "events": {
                "ready": "app.addFetchValueButton(this);"
              }
            },
            {
              "_icon": "combo",
              "text": "autoLoadSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "autoLoadSelect",
                "text": "Auto Load",
                "url": "demo-source?xaction=staffRows",
                "textField": "full_name",
                "valueField": "code",
                "autoLoad": "all",
                "value": "'10003'",
                "subtextField": "code",
                "noWrap": "true"
              }
            },
            {
              "_icon": "combo",
              "text": "liveSearchSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "liveSearchSelect",
                "text": "Live Seach",
                "url": "demo-source?xaction=staffSearch",
                "textField": "full_name",
                "valueField": "code"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "templateSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "templateSelect",
                "text": "Template Select",
                "url": "demo-source?xaction=staffRows",
                "loadOnce": "true",
                "itemTpl": "<div class=\"w-row w-align-center\">\n  <div style=\"width: 3em\" class=\"w-cell w-rownum\"></div>\n  <div class=\"w-icon icon-user2\"></div>\n  <div class=\"w-cell w-flex\">{code}</div>\n  <div class=\"w-cell w-flex3\">{full_name}</div>\n</div>",
                "pickerMinWidth": "20em",
                "cls": "w-rownum-reset",
                "textField": "full_name",
                "valueField": "code"
              },
              "events": {
                "ready": "app.addFetchValueButton(this);"
              }
            },
            {
              "_icon": "combo",
              "text": "templateFnSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "templateFnSelect",
                "text": "Template Function",
                "url": "demo-source?xaction=staffRows",
                "loadOnce": "true",
                "pickerMinWidth": "20em",
                "cls": "w-rownum-reset",
                "textField": "full_name",
                "valueField": "code",
                "itemTplFn": "return `<div class=\"w-row w-align-center\">\n  <div style=\"width: 3em\" class=\"w-cell w-rownum\"></div>\n  <div class=\"w-icon icon-user2\"></div>\n  <div class=\"w-cell w-flex\">${data.code}</div>\n  <div class=\"w-cell w-flex3\">${data.full_name}</div>\n</div>`;"
              },
              "events": {
                "ready": "app.addFetchValueButton(this);"
              }
            },
            {
              "_icon": "combo",
              "text": "keyNameSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "keyNameSelect",
                "text": "Key Name",
                "keyName": "status"
              },
              "events": {
                "ready": "app.addFetchValueButton(this);"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "forceSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "forceSelect",
                "text": "Force Select",
                "forceSelect": "true",
                "url": "demo-source?xaction=staffSearch",
                "valueField": "code",
                "textField": "full_name"
              },
              "_expanded": true,
              "events": {
                "ready": "app.addFetchValueButton(this);"
              },
              "items": [
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "//When the remote drop-down list data has not yet been loaded, we can specify both the value and text\nthis.parent.value = ['10003', 'Michael Sullivan'];"
                  },
                  "properties": {
                    "cid": "triggers",
                    "icon": "data-provider",
                    "tip": "Set value"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "combo",
              "text": "customMatchSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "customMatchSelect",
                "text": "Custom Match",
                "data": "['item1 abc item1', 'item2 abc item2', 'item3 def item3']",
                "matchMode": "includesIC",
                "selectMode": "startsWithIC"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "miscSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "miscSelect",
                "data": "[\n  { text: 'With icon', _icon: 'user2' },\n  { text: 'With image', _img: 'add' },\n  { text: 'With tip', _tip: 'Item tip' },\n  { text: 'Nowrap line with overflow text', _cls: 'w-nowrap' },\n  { text: 'Fixed item', _fixed: true },\n  { text: 'Disabled Item', _disabled: true }\n]",
                "text": "Miscellaneous"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "bindFieldSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "bindFieldSelect",
                "text": "Bind Field",
                "bindField": "myValueField",
                "url": "demo-source?xaction=staffSearch",
                "textField": "full_name",
                "valueField": "code"
              },
              "events": {
                "ready": "let select = this;\nselect.addTrigger({\n  icon: 'share', tip: 'Show values', weight: 991, handler() {\n    Wb.tip(Wb.encode(Wb.getValue(select)));\n  }\n});"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "Wb.setValue(app.viewport1, { myValueField: '10003', bindFieldSelect: 'Michael Sullivan' });"
                  },
                  "properties": {
                    "cid": "triggers",
                    "icon": "data-provider",
                    "tip": "Set value"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "combo",
              "text": "valueMapSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "valueMapSelect",
                "text": "Value Map",
                "url": "demo-source?xaction=staffSearch",
                "textField": "full_name",
                "valueMap": "{code: 'my_code', email: 'my_email', full_name: 'valueMapSelect' }",
                "gridColumn": "1 / -1",
                "subtextField": "code"
              },
              "events": {
                "ready": "let select = this;\nselect.addTrigger({\n  icon: 'share', tip: 'Show values', weight: 991, handler() {\n    Wb.tip(Wb.encode(Wb.getValue(select)));\n  }\n});"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "Wb.setValue(app.valueMapSelect, { my_code: '10003', my_email: 'michael8@geejing.com', valueMapSelect: 'Michael Sullivan' });"
                  },
                  "properties": {
                    "cid": "triggers",
                    "icon": "data-provider",
                    "tip": "Set value"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "combo",
              "text": "multiSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "multiSelect",
                "text": "Multi Select",
                "url": "demo-source?xaction=staffRows",
                "textField": "full_name",
                "valueField": "code",
                "noWrap": "true",
                "multiSelect": "true",
                "gridColumn": "1 / -1",
                "clearButton": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "triggers"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "this.parent.value = [{ code: '10001', full_name: 'Terri Duffy' }, { code: '10005', full_name: 'Gail Erickson' },\n{ code: '10008', full_name: 'Janice Galvin' }];"
                      },
                      "properties": {
                        "cid": "setBtn",
                        "icon": "data-provider",
                        "tip": "Set values"
                      },
                      "text": "setBtn",
                      "_expanded": false,
                      "_icon": "item",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "Wb.tip(Wb.encode(this.parent.value));"
                      },
                      "properties": {
                        "cid": "getBtn",
                        "icon": "share",
                        "tip": "Get values"
                      },
                      "text": "getBtn",
                      "_expanded": true,
                      "_icon": "item",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "combo",
              "text": "multiSelectWrap",
              "cls": "Wb.Select",
              "properties": {
                "cid": "multiSelectWrap",
                "text": "Multi Select Wrap",
                "url": "demo-source?xaction=staffSearch",
                "textField": "full_name",
                "valueField": "code",
                "noWrap": "true",
                "multiSelect": "true",
                "gridColumn": "1 / -1",
                "tagSortable": "true",
                "tagWrap": "true",
                "clearButton": "true",
                "tagTipField": "email",
                "subtextField": "code",
                "maxHeight": "10em",
                "tagWidth": "calc(100% - 8em)"
              },
              "_expanded": true
            },
            {
              "_icon": "combo",
              "text": "treeSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "treeSelect",
                "text": "Tree Select",
                "textField": "text",
                "valueField": "sid",
                "gridColumn": "1 / -1",
                "treePicker": "true",
                "data": "[\n  {\n    text: 'Asia', _icon: 'flag1', myCheck: 1, sid: 'a',\n    remark: 'Asia is the largest continent in the world', area: 44579000, percentage: 0.3, items: [\n      { text: 'China', _leaf: true, myCheck: 0, sid: 'a1' },\n      { text: 'Japan', _leaf: true, myCheck: 1, sid: 'a2' },\n      { text: 'Korea', _leaf: true, myCheck: 0, sid: 'a3' }\n    ]\n  },\n  {\n    text: 'Europe', _icon: 'flag1', myCheck: 1, sid: 'b',\n    remark: 'Europe is between Asia and Africa', area: 9938000, percentage: 0.067, items: [\n      { text: 'France', _leaf: true, sid: 'b1' },\n      { text: 'Germany', _leaf: true, sid: 'b2' },\n      { text: 'UK', _leaf: true, sid: 'b3' }\n    ]\n  },\n  {\n    text: 'North America', _icon: 'flag1', _selected: true, _expanded: true, sid: 'c',\n    remark: \"North America is the third largest of the world's continents\", area: 24256000, percentage: 0.165, items: [\n      { text: 'Canada', _leaf: true, sid: 'c1' },\n      { text: 'Mexico', _leaf: true, sid: 'c2' },\n      { text: 'USA', _leaf: true, sid: 'c3' }\n    ]\n  },\n  {\n    text: 'South America', _icon: 'flag1', _disabled: true, sid: 'd',\n    remark: \"Sorth America is the fourth largest of the world's continents\", area: 17819000, percentage: 0.12, items: [\n      { text: 'Argentina', _leaf: true, sid: 'd1' },\n      { text: 'Brazil', _leaf: true, sid: 'd2' }\n    ]\n  }\n]"
              },
              "events": {
                "beforeselect": "if (!item.leaf) {\n  Wb.tip('Please select a leaf node');\n  return false;\n}"
              }
            },
            {
              "_icon": "combo",
              "text": "singleTagTreeSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "singleTagTreeSelect",
                "text": "Single Tag Tree",
                "url": "demo-source?xaction=listAreaTree1",
                "textField": "text",
                "valueField": "sid",
                "gridColumn": "1 / -1",
                "clearButton": "true",
                "treePicker": "true",
                "useTag": "true"
              },
              "events": {
                "change": "// Wb.tip(Wb.encode(value));"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "triggers"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "this.parent.value = { text: 'Paris', subtext: '004LMY1FA9D40', sid: '004LMY1FA9D40' };"
                      },
                      "properties": {
                        "cid": "setBtn",
                        "icon": "data-provider",
                        "tip": "Set values"
                      },
                      "text": "setBtn",
                      "_expanded": false,
                      "_icon": "item",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "Wb.tip(Wb.encode(this.parent.value));"
                      },
                      "properties": {
                        "cid": "getBtn",
                        "icon": "share",
                        "tip": "Get values"
                      },
                      "text": "getBtn",
                      "_expanded": true,
                      "_icon": "item",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "combo",
              "text": "multiTreeSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "multiTreeSelect",
                "text": "Multi Select Tree",
                "url": "demo-source?xaction=listAreaTree1",
                "textField": "text",
                "valueField": "sid",
                "gridColumn": "1 / -1",
                "tagSortable": "true",
                "tagWrap": "true",
                "clearButton": "true",
                "treePicker": "true",
                "maxHeight": "10em",
                "tagWidth": "70%",
                "multiSelect": "true"
              },
              "events": {
                "change": "// Wb.tip(Wb.encode(value));"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "triggers"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "this.parent.value = [\n  { text: 'Lyon', subtext: '004LMY1FA9D41', sid: '004LMY1FA9D41' },\n  { text: 'Paris', subtext: '004LMY1FA9D40', sid: '004LMY1FA9D40' }\n];"
                      },
                      "properties": {
                        "cid": "setBtn",
                        "icon": "data-provider",
                        "tip": "Set values"
                      },
                      "text": "setBtn",
                      "_expanded": false,
                      "_icon": "item",
                      "hasDupCid": 1
                    },
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "Wb.tip(Wb.encode(this.parent.value));"
                      },
                      "properties": {
                        "cid": "getBtn",
                        "icon": "share",
                        "tip": "Get values"
                      },
                      "text": "getBtn",
                      "_expanded": true,
                      "_icon": "item",
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "linkedSelectTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "linkedSelectTitle",
            "title": "Linked Select"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "linkedSelectCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "linkedSelectCt",
            "layout": "grid",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "combo",
              "text": "continentSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "continentSelect",
                "placeholder": "Continent",
                "forceSelect": "true",
                "url": "demo-source?xaction=listRootArea",
                "textField": "area_name",
                "valueField": "sid",
                "required": "true"
              },
              "events": {
                "ready": "let me = this;\n//Pick up the first item\nme.picker.load({ success() { me.valueIndex = 0; } });"
              }
            },
            {
              "_icon": "combo",
              "text": "countrySelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "countrySelect",
                "placeholder": "Country",
                "forceSelect": "true",
                "url": "demo-source?xaction=listArea",
                "textField": "area_name",
                "valueField": "sid",
                "required": "true"
              },
              "events": {
                "beforeload": "let select = app.continentSelect;\nif (select.verify())\n  params.parent_id = select.value;\nelse {\n  select.focus();\n  return false;\n}"
              }
            },
            {
              "_icon": "combo",
              "text": "citySelect",
              "cls": "Wb.Select",
              "_expanded": true,
              "properties": {
                "cid": "citySelect",
                "placeholder": "City",
                "forceSelect": "true",
                "url": "demo-source?xaction=listArea",
                "textField": "area_name",
                "valueField": "sid",
                "required": "true"
              },
              "events": {
                "beforeload": "let select = app.countrySelect;\nif (select.verify())\n  params.parent_id = select.value;\nelse {\n  select.focus();\n  return false;\n}"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "triggerTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "triggerTitle",
            "title": "Trigger"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "triggerCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "triggerCt",
            "layout": "grid",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "trigger",
              "text": "commonTrigger",
              "cls": "Wb.Trigger",
              "properties": {
                "cid": "commonTrigger",
                "text": "Common Trigger"
              },
              "events": {
                "triggerclick": "Wb.tip(this.cid + ' triggered');"
              }
            },
            {
              "_icon": "trigger",
              "text": "readonlyTrigger",
              "cls": "Wb.Trigger",
              "properties": {
                "cid": "readonlyTrigger",
                "text": "Readonly Trigger",
                "editable": "false",
                "enterTriggerClick": "true",
                "triggerIcon": "location",
                "placeholder": "Press enter"
              },
              "events": {
                "triggerclick": "Wb.tip(this.cid + ' triggered');"
              }
            },
            {
              "_icon": "trigger",
              "text": "extraTrigger",
              "cls": "Wb.Trigger",
              "properties": {
                "cid": "extraTrigger",
                "text": "Extra Triggers"
              },
              "events": {
                "triggerclick": "Wb.tip('Default trigger clicked');"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "triggers"
                  },
                  "text": "triggers",
                  "_expanded": true,
                  "_icon": "array",
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "Wb.tip('Set trigger clicked');"
                      },
                      "properties": {
                        "cid": "setItem",
                        "icon": "gear",
                        "tip": "@Str.set"
                      },
                      "text": "setItem",
                      "_expanded": true,
                      "_icon": "item"
                    },
                    {
                      "cls": "Wb.Item",
                      "properties": {
                        "cid": "copyItem",
                        "icon": "copy",
                        "tip": "@Str.copy"
                      },
                      "text": "copyItem",
                      "_expanded": true,
                      "_icon": "item"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "pickerTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "pickerTitle",
            "title": "Picker"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "pickerCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "pickerCt",
            "layout": "grid",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "picker",
              "text": "commonPicker",
              "cls": "Wb.Picker",
              "_expanded": true,
              "properties": {
                "cid": "commonPicker",
                "text": "Common Picker",
                "editable": "false"
              },
              "events": {
                "collapse": "this.value = Wb.encode(Wb.getValue(this.picker));",
                "expand": "Wb.setValue(this.picker, this.value ? Wb.decode(this.value) : {});"
              },
              "items": [
                {
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "picker",
                    "isProperty": "true",
                    "layout": "grid1",
                    "defaults": "({ labelWidth: '5em' })"
                  },
                  "text": "picker",
                  "_expanded": true,
                  "_icon": "container",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "text",
                      "text": "nameText",
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "nameText",
                        "text": "name"
                      }
                    },
                    {
                      "_icon": "text",
                      "text": "typeText",
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "typeText",
                        "text": "type"
                      }
                    },
                    {
                      "_icon": "number-edit",
                      "text": "countNumber",
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "countNumber",
                        "text": "Count"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "picker",
              "text": "gridPicker",
              "cls": "Wb.Picker",
              "_expanded": true,
              "properties": {
                "cid": "gridPicker",
                "text": "Grid Picker"
              },
              "items": [
                {
                  "_icon": "grid",
                  "text": "picker",
                  "cls": "Wb.Grid",
                  "properties": {
                    "cid": "picker",
                    "isProperty": "true",
                    "url": "demo-source?xaction=staffDict"
                  },
                  "hasDupCid": 0
                }
              ]
            },
            {
              "_icon": "picker",
              "text": "treePicker",
              "cls": "Wb.Picker",
              "_expanded": true,
              "properties": {
                "cid": "treePicker",
                "text": "Tree Picker",
                "editable": "false"
              },
              "events": {
                "collapse": "this.value = this.picker.checkedNodes.pluck('text').join(', ');"
              },
              "items": [
                {
                  "_icon": "tree-view",
                  "text": "picker",
                  "cls": "Wb.Tree",
                  "_expanded": true,
                  "properties": {
                    "cid": "picker",
                    "isProperty": "true",
                    "url": "demo-source?xaction=listAreaTree"
                  },
                  "events": {
                    "itemclick": "let picker = this.parent;\nif (item.leaf) {\n  picker.setValue(item.data.area_name);\n  picker.collapse();\n  picker.focus();\n}"
                  },
                  "hasDupCid": 0
                }
              ]
            },
            {
              "_icon": "picker",
              "text": "lazyPicker",
              "cls": "Wb.Picker",
              "_expanded": true,
              "properties": {
                "cid": "lazyPicker",
                "text": "Lazy Picker",
                "lazyPicker": "true"
              },
              "items": [
                {
                  "_icon": "container",
                  "text": "picker",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "picker",
                    "text": "Load large data asynchronously",
                    "isProperty": "true",
                    "frame": "true",
                    "padding": "true"
                  },
                  "hasDupCid": 0
                }
              ]
            },
            {
              "_icon": "calendar",
              "text": "dockedPicker",
              "cls": "Wb.Date",
              "_expanded": true,
              "properties": {
                "cid": "dockedPicker",
                "text": "Picker docked bottom",
                "dockedPicker": "true",
                "shareView": "false"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "othersTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "othersTitle",
            "title": "Others"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "othersCt",
          "cls": "Wb.Container",
          "_expanded": true,
          "properties": {
            "cid": "othersCt",
            "layout": "grid",
            "frame": "true"
          },
          "items": [
            {
              "_icon": "color",
              "text": "color1",
              "cls": "Wb.Color",
              "properties": {
                "cid": "color1",
                "placeholder": "Color"
              },
              "_expanded": true
            },
            {
              "_icon": "brush",
              "text": "colorSelect1",
              "cls": "Wb.ColorSelect",
              "properties": {
                "cid": "colorSelect1"
              }
            },
            {
              "_icon": "check8",
              "text": "boolSelect1",
              "cls": "Wb.BoolSelect",
              "properties": {
                "cid": "boolSelect1",
                "placeholder": "Bool selector"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "codingTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "codingTitle",
            "title": "Coding"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "codingCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "codingCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "demoCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "demoCt",
                "layout": "form-compact"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "combo",
                  "text": "localSelect",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "localSelect",
                    "text": "Local Select",
                    "data": "['item1', 'item2', 'item3']"
                  }
                },
                {
                  "_icon": "combo",
                  "text": "remoteSelect1",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "remoteSelect1",
                    "text": "Remote Select",
                    "url": "demo-source?xaction=staffRows",
                    "textField": "full_name",
                    "valueField": "code",
                    "loadOnce": "true"
                  },
                  "events": {
                    "beforeload": "params.foo = 'bar';"
                  }
                }
              ]
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "_expanded": false,
              "properties": {
                "cid": "line1",
                "title": "Demonstrate",
                "dimTitle": "true",
                "dashed": "true"
              }
            },
            {
              "_icon": "container",
              "text": "actionCt",
              "cls": "Wb.Container",
              "_expanded": true,
              "properties": {
                "cid": "actionCt",
                "layout": "form-compact"
              },
              "items": [
                {
                  "_icon": "button",
                  "text": "addBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "addBtn",
                    "text": "Insert Item"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.localSelect.data.push({ text: 'New Item' }); //add permanently\n//app.localSelect.picker.addData({ text: 'New Item' }); //add transiently because the picker data will be reloaded when expand it"
                  }
                },
                {
                  "_icon": "button",
                  "text": "removeBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "removeBtn",
                    "text": "Remove Item"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.localSelect.data.erase(0); //erase is a function ofArray, see Array functions"
                  }
                },
                {
                  "_icon": "button",
                  "text": "expandLocalBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "expandLocalBtn",
                    "text": "Expand Local Selet"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.localSelect.expand();"
                  }
                },
                {
                  "_icon": "button",
                  "text": "expandRemoteBtn",
                  "cls": "Wb.Button",
                  "properties": {
                    "cid": "expandRemoteBtn",
                    "text": "Expand Remote Select"
                  },
                  "_expanded": true,
                  "events": {
                    "click": "app.remoteSelect1.query(); //expand function has no effect because the picker's data has not been loaded yet"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: slot.xwl


{
  "title": "",
  "icon": "slots",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "description": "demo-source=example/common/demo-source.xwl"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Slot",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "slotListTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "slotListTitle",
            "title": "Slot list"
          }
        },
        {
          "_icon": "container",
          "text": "slotListCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "slotListCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonSlotLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonSlotLabel",
                "text": "Common slot"
              }
            },
            {
              "_icon": "slots",
              "text": "commonSlot",
              "cls": "Wb.Slot",
              "properties": {
                "cid": "commonSlot",
                "data": "[\n  [{ text: 'a1' }, { text: 'a2' }, { text: 'a3' }, { text: 'a4' }, { text: 'a5' }, { text: 'a6' }],\n  [{ text: 'b1' }, { text: 'b2' }, { text: 'b3' }, { text: 'b4' }, { text: 'b5' }, { text: 'b6' }]\n]",
                "buttonAlign": "center"
              },
              "events": {
                "itemselect": "Wb.tip(Wb.encode(this.value) + ' selected');"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Array",
                  "properties": {
                    "cid": "buttons"
                  },
                  "text": "buttons",
                  "_expanded": true,
                  "_icon": "array",
                  "items": [
                    {
                      "cls": "Wb.Button",
                      "events": {
                        "click": "app.commonSlot.value = ['a2', 'b2'];"
                      },
                      "properties": {
                        "cid": "setValueBtn",
                        "text": "Set value"
                      },
                      "text": "setValueBtn",
                      "_expanded": true,
                      "_icon": "button"
                    },
                    {
                      "cls": "Wb.Button",
                      "events": {
                        "click": "Wb.tip(this.cid + ' values is: ' + Wb.encode(app.commonSlot.value));"
                      },
                      "properties": {
                        "cid": "getValueBtn",
                        "text": "Get value"
                      },
                      "text": "getValueBtn",
                      "_expanded": true,
                      "_icon": "button"
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "label",
              "text": "customSlotLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "customSlotLabel",
                "text": "Custom slot"
              }
            },
            {
              "_icon": "slots",
              "text": "customSlot",
              "cls": "Wb.Slot",
              "properties": {
                "cid": "customSlot",
                "height": "15em"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "list",
                  "text": "slotView1",
                  "cls": "Wb.SlotView",
                  "properties": {
                    "cid": "slotView1",
                    "url": "demo-source?xaction=staffDict",
                    "textField": "full_name",
                    "valueField": "code",
                    "topTitle": "Staff list"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "linkedSlotTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "linkedSlotTitle",
            "title": "Linked slot without scrollbar"
          }
        },
        {
          "_icon": "slots",
          "text": "linkedSlot",
          "cls": "Wb.Slot",
          "properties": {
            "cid": "linkedSlot",
            "height": "10em",
            "defaults": "({ autoScroll: 'hide' })",
            "border": "false"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "list",
              "text": "continentView",
              "cls": "Wb.SlotView",
              "properties": {
                "cid": "continentView",
                "url": "demo-source?xaction=listRootArea",
                "topTitle": "Continent",
                "textField": "area_name"
              },
              "events": {
                "selectionchange": "app.countryView.load({ params: { parent_id: this.selection.data.sid } });"
              },
              "_expanded": true
            },
            {
              "_icon": "list",
              "text": "countryView",
              "cls": "Wb.SlotView",
              "properties": {
                "cid": "countryView",
                "url": "demo-source?xaction=listArea",
                "topTitle": "Country",
                "textField": "area_name"
              },
              "events": {
                "selectionchange": "app.cityView.load({ params: { parent_id: this.selection?.data.sid } });"
              }
            },
            {
              "_icon": "list",
              "text": "cityView",
              "cls": "Wb.SlotView",
              "properties": {
                "cid": "cityView",
                "url": "demo-source?xaction=listArea",
                "topTitle": "City",
                "textField": "area_name"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: toolbar-menu.xwl


{
  "title": "",
  "icon": "toolbar",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "array",
      "text": "components",
      "cls": "Wb.Array",
      "properties": {
        "cid": "components"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "menu2",
          "text": "sharedContextMenu",
          "cls": "Wb.Menu",
          "properties": {
            "cid": "sharedContextMenu"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "item",
              "text": "showTargetItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "showTargetItem",
                "text": "Show target",
                "icon": "cube"
              },
              "events": {
                "click": "Wb.tip('Popup context menu from ' + this.contextTarget.getClosestComp().cid);"
              }
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line1"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "item",
              "text": "item1",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item1",
                "text": "@Str.add",
                "icon": "add"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "item",
              "text": "item2",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "item2",
                "text": "@Str.edit",
                "icon": "edit"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "item",
              "text": "item3",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "item3",
                "text": "@Str.del",
                "icon": "delete"
              },
              "hasDupCid": 1
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Toolbar and menu",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "toolbarTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "toolbarTitle",
            "title": "Toolbar"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "toolbarCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "toolbarCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonToolBarLabel",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "commonToolBarLabel",
                "text": "Common toolbar"
              }
            },
            {
              "_icon": "toolbar",
              "text": "commonToolbar",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "commonToolbar",
                "border": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "addItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addItem",
                    "icon": "add",
                    "text": "@Str.add",
                    "tip": "Add new item"
                  }
                },
                {
                  "_icon": "item",
                  "text": "deleteItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "deleteItem",
                    "icon": "delete",
                    "text": "@Str.del"
                  }
                },
                {
                  "_icon": "item",
                  "text": "shortCutKeyItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "shortCutKeyItem",
                    "text": "Bind key",
                    "icon": "keyboard",
                    "keys": "Ctrl+K"
                  },
                  "events": {
                    "click": "Wb.tip(this.cid + ' clicked');"
                  }
                },
                {
                  "_icon": "item",
                  "text": "disabledItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "disabledItem",
                    "icon": "property",
                    "text": "Disabled",
                    "disabled": "true"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "menuItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "menuItem",
                    "icon": "menu",
                    "text": "Menu"
                  },
                  "items": [
                    {
                      "cls": "Wb.Menu",
                      "properties": {
                        "cid": "menu",
                        "isProperty": "true"
                      },
                      "text": "menu",
                      "_expanded": true,
                      "_icon": "menu2",
                      "hasDupCid": 0,
                      "items": [
                        {
                          "_icon": "item",
                          "text": "item1",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "item1",
                            "text": "Menu item 1"
                          },
                          "hasDupCid": 1
                        },
                        {
                          "_icon": "item",
                          "text": "item2",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "item2",
                            "text": "Menu item 2"
                          },
                          "hasDupCid": 1
                        },
                        {
                          "_icon": "item",
                          "text": "item3",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "item3",
                            "text": "Menu item 3"
                          },
                          "hasDupCid": 1
                        }
                      ]
                    }
                  ]
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "trigger",
                  "text": "trigger1",
                  "cls": "Wb.Trigger",
                  "properties": {
                    "cid": "trigger1",
                    "triggerIcon": "module"
                  },
                  "events": {
                    "triggerclick": "Wb.tip(this.cid + ' clicked');"
                  }
                },
                {
                  "_icon": "item",
                  "text": "centerAlignItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "centerAlignItem",
                    "icon": "data",
                    "alignSelf": "center",
                    "tip": "Centered button"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "right1",
                  "text": "fill1",
                  "cls": "Wb.Fill",
                  "properties": {
                    "cid": "fill1"
                  }
                },
                {
                  "_icon": "item",
                  "text": "fillRightItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "fillRightItem",
                    "icon": "calendar"
                  },
                  "hasDupCid": 0
                }
              ]
            },
            {
              "_icon": "label",
              "text": "overflowedToolbarLabel",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "overflowedToolbarLabel",
                "text": "Overflowed toolbar"
              }
            },
            {
              "_icon": "toolbar",
              "text": "overflowedToolbar",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "overflowedToolbar",
                "border": "true",
                "width": "25em",
                "overflowMenu": "true",
                "resizable": "true",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "text": "item 1",
                    "icon": "calculator"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "text": "item 2",
                    "icon": "add1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "menuTextItem",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "menuTextItem",
                    "icon": "broadcast",
                    "menuText": "MenuText Item"
                  }
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item3",
                    "text": "item 3",
                    "icon": "click"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item4",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item4",
                    "text": "item 4",
                    "icon": "lantern"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "item",
                  "text": "item5",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item5",
                    "text": "item 5",
                    "icon": "mail"
                  }
                }
              ]
            },
            {
              "_icon": "label",
              "text": "centeredShrinkToolbarLabel",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "centeredShrinkToolbarLabel",
                "text": "Centered shrink toolbar"
              }
            },
            {
              "_icon": "toolbar",
              "text": "centeredShrinkToolbar",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "centeredShrinkToolbar",
                "justify": "center",
                "border": "true",
                "resizable": "true",
                "shrink": "true",
                "width": "25em",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "book1",
                    "text": "Item1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "card",
                    "text": "Item2"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item3",
                    "icon": "cube",
                    "text": "Item3"
                  },
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "label",
              "text": "verticalToolbarLabel",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "verticalToolbarLabel",
                "text": "Vertical toolbar"
              }
            },
            {
              "_icon": "toolbar",
              "text": "verticalToolbar",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "verticalToolbar",
                "frame": "true",
                "vertical": "true",
                "maxWidth": "10em"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "book1",
                    "text": "Item1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "card",
                    "text": "Item2 Card"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item3",
                    "icon": "cube",
                    "text": "Item3"
                  },
                  "hasDupCid": 1
                }
              ]
            },
            {
              "_icon": "label",
              "text": "menuStyleToolbarLabel",
              "cls": "Wb.Label",
              "_expanded": true,
              "properties": {
                "cid": "menuStyleToolbarLabel",
                "text": "Menu style toolbar"
              }
            },
            {
              "_icon": "toolbar",
              "text": "menuStyleToolbar",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "menuStyleToolbar",
                "frame": "true",
                "type": "menu",
                "maxWidth": "10em"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "book1",
                    "text": "Item1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "card",
                    "text": "Item2 Card"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item3",
                    "icon": "cube",
                    "text": "Item3"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "menuTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "menuTitle",
            "title": "Menu"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "menuCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "menuCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "toolbar",
              "text": "toolbar1",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "toolbar1",
                "layout": "center",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item1",
                    "text": "Menu Item"
                  },
                  "hasDupCid": 1,
                  "items": [
                    {
                      "cls": "Wb.Menu",
                      "properties": {
                        "cid": "menu",
                        "isProperty": "true"
                      },
                      "text": "menu",
                      "_expanded": true,
                      "_icon": "menu2",
                      "hasDupCid": 0,
                      "items": [
                        {
                          "_icon": "item",
                          "text": "iconItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "iconItem",
                            "text": "Menu item 1",
                            "icon": "briefcase"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "imgItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "imgItem",
                            "text": "Menu item 1",
                            "img": "sitemap"
                          }
                        },
                        {
                          "_icon": "divider",
                          "text": "divider1",
                          "cls": "Wb.Divider",
                          "properties": {
                            "cid": "divider1"
                          },
                          "hasDupCid": 1
                        },
                        {
                          "_icon": "item",
                          "text": "disabledItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "disabledItem",
                            "disabled": "true",
                            "icon": "download",
                            "text": "Disabled"
                          },
                          "hasDupCid": 1
                        },
                        {
                          "_icon": "container",
                          "text": "container1",
                          "cls": "Wb.Container",
                          "properties": {
                            "cid": "container1",
                            "layout": "row",
                            "gap": ".2em",
                            "cls": "w-mi-margin"
                          },
                          "_expanded": true,
                          "items": [
                            {
                              "_icon": "slidebar",
                              "text": "fsSlider",
                              "cls": "Wb.Slider",
                              "properties": {
                                "cid": "fsSlider",
                                "text": "@Str.fontSize",
                                "maxValue": "50",
                                "flex": "1",
                                "minValue": "12",
                                "tabIndex": "null",
                                "focusable": "false"
                              },
                              "_expanded": true
                            },
                            {
                              "_icon": "button",
                              "text": "resetBtn",
                              "cls": "Wb.Button",
                              "properties": {
                                "cid": "resetBtn",
                                "tip": "@Str.reset",
                                "icon": "refresh",
                                "type": "tool",
                                "tabIndex": "undefined",
                                "focusable": "false"
                              }
                            }
                          ]
                        },
                        {
                          "_icon": "item",
                          "text": "bindKeyItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "bindKeyItem",
                            "text": "Bind key item",
                            "keys": "Ctrl+L"
                          },
                          "events": {
                            "click": "Wb.tip(this.cid + ' clicked');"
                          }
                        },
                        {
                          "_icon": "space",
                          "text": "space1",
                          "cls": "Wb.Space",
                          "properties": {
                            "cid": "space1"
                          }
                        },
                        {
                          "_icon": "item",
                          "text": "subMenuItem",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "subMenuItem",
                            "text": "Sub menu"
                          },
                          "items": [
                            {
                              "cls": "Wb.Menu",
                              "properties": {
                                "cid": "menu",
                                "isProperty": "true"
                              },
                              "text": "menu",
                              "_expanded": true,
                              "_icon": "menu2",
                              "hasDupCid": 0,
                              "items": [
                                {
                                  "_icon": "item",
                                  "text": "item1",
                                  "cls": "Wb.Item",
                                  "_expanded": false,
                                  "properties": {
                                    "cid": "item1",
                                    "text": "Item 1"
                                  },
                                  "hasDupCid": 1
                                },
                                {
                                  "_icon": "item",
                                  "text": "item2",
                                  "cls": "Wb.Item",
                                  "_expanded": false,
                                  "properties": {
                                    "cid": "item2",
                                    "text": "Item 2"
                                  },
                                  "hasDupCid": 1
                                },
                                {
                                  "_icon": "item",
                                  "text": "item3",
                                  "cls": "Wb.Item",
                                  "_expanded": true,
                                  "properties": {
                                    "cid": "item3",
                                    "text": "Item 3"
                                  },
                                  "hasDupCid": 1
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "button",
              "text": "menuButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "menuButton",
                "text": "Menu Button",
                "icon": "menu"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "uncheckItem",
                      "cls": "Wb.Item",
                      "_expanded": false,
                      "properties": {
                        "cid": "uncheckItem",
                        "text": "Unchecked item",
                        "checked": "false"
                      },
                      "events": {
                        "checkchange": "Wb.tip(this.cid + ' checked is ' + checked);"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "checkedItem",
                      "cls": "Wb.Item",
                      "_expanded": false,
                      "properties": {
                        "cid": "checkedItem",
                        "text": "Checked Item",
                        "checked": "true"
                      }
                    },
                    {
                      "_icon": "line",
                      "text": "line1",
                      "cls": "Wb.Line",
                      "properties": {
                        "cid": "line1"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "groupedItem1",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "groupedItem1",
                        "text": "Grouped item1",
                        "checkGroup": "group1",
                        "checked": "true"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "groupedItem2",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "groupedItem2",
                        "text": "Grouped item2",
                        "checkGroup": "group1",
                        "checked": "false"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "groupedItem3",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "groupedItem3",
                        "text": "Grouped item3",
                        "checkGroup": "group1",
                        "checked": "false"
                      }
                    },
                    {
                      "_icon": "line",
                      "text": "line2",
                      "cls": "Wb.Line",
                      "_expanded": true,
                      "properties": {
                        "cid": "line2"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "ellipsisItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "ellipsisItem",
                        "ellipsis": "true",
                        "text": "Ellipsis Item (means to show dialog)"
                      }
                    },
                    {
                      "_icon": "line",
                      "text": "line3",
                      "cls": "Wb.Line",
                      "_expanded": true,
                      "properties": {
                        "cid": "line3"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "noHideItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "noHideItem",
                        "text": "Click not to hide menu",
                        "clickHide": "false"
                      },
                      "events": {
                        "click": "Wb.tip('clicked');"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "container",
              "text": "contextMenuCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "contextMenuCt",
                "layout": "center",
                "text": "Please click the mouse context menu button to show context menu",
                "width": "20em",
                "height": "5em",
                "padding": "true",
                "textSelectable": "false",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "contextMenu",
                    "isProperty": "true"
                  },
                  "text": "contextMenu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "item1",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item1",
                        "text": "Menu item 1",
                        "icon": "book1"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item2",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "item2",
                        "text": "Menu item 2"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "item3",
                      "cls": "Wb.Item",
                      "_expanded": false,
                      "properties": {
                        "cid": "item3",
                        "text": "Menu item 3"
                      },
                      "hasDupCid": 1
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "container",
              "text": "sharedContextMenuCt1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "sharedContextMenuCt1",
                "layout": "center",
                "text": "Shared context menu comp1",
                "height": "5em",
                "padding": "true",
                "contextMenu": "app.sharedContextMenu",
                "frame": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "container",
              "text": "sharedContextMenuCt2",
              "cls": "Wb.Container",
              "properties": {
                "cid": "sharedContextMenuCt2",
                "layout": "center",
                "text": "Shared context menu comp2",
                "height": "5em",
                "padding": "true",
                "contextMenu": "app.sharedContextMenu",
                "frame": "true"
              },
              "_expanded": true
            }
          ]
        }
      ]
    }
  ]
}

File name: tree.xwl


{
  "title": "",
  "icon": "tree-view",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "description": "demo-source=example/common/demo-source.xwl"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Tree",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "commonRemoteTreeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commonRemoteTreeTitle",
            "title": "Common remote tree"
          }
        },
        {
          "_icon": "tree-view",
          "text": "commonRemoteTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "commonRemoteTree",
            "url": "demo-source?xaction=listAreaTree",
            "height": "15em"
          }
        },
        {
          "_icon": "title",
          "text": "miscTreeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "miscTreeTitle",
            "title": "Miscillaneous tree"
          }
        },
        {
          "_icon": "tree-view",
          "text": "miscTree",
          "cls": "Wb.Tree",
          "_expanded": false,
          "properties": {
            "cid": "miscTree",
            "localData": "[\n  {\n    text: 'Asia', _icon: 'flag1', _checked: true, myCheck: 1,\n    remark: 'Asia is the largest continent in the world', area: 44579000, percentage: 0.3, items: [\n      { text: 'China', _leaf: true, _checked: true, myCheck: 0 },\n      { text: 'Japan', _leaf: true, _checked: true, myCheck: 1 },\n      { text: 'Korea', _leaf: true, _checked: true, myCheck: 0 }\n    ]\n  },\n  {\n    text: 'Europe', _icon: 'flag1', myCheck: 1,\n    remark: 'Europe is between Asia and Africa', area: 9938000, percentage: 0.067, items: [\n      { text: 'France', _leaf: true },\n      { text: 'Germany', _leaf: true },\n      { text: 'UK', _leaf: true }\n    ]\n  },\n  {\n    text: 'North America', _icon: 'flag1', _selected: true, _expanded: true,\n    remark: \"North America is the third largest of the world's continents\", area: 24256000, percentage: 0.165, items: [\n      { text: 'Canada', _leaf: true },\n      { text: 'Mexico', _leaf: true },\n      { text: 'USA', _leaf: true }\n    ]\n  },\n  {\n    text: 'South America', _icon: 'flag1', _disabled: true,\n    remark: \"Sorth America is the fourth largest of the world's continents\", area: 17819000, percentage: 0.12, items: [\n      { text: 'Argentina', _leaf: true },\n      { text: 'Brazil', _leaf: true }\n    ]\n  }\n]",
            "height": "20em",
            "pagingBar": "true",
            "resizable": "true",
            "gridLine": "true",
            "columnsSortable": "true",
            "sorters": "text",
            "stateId": "wb.miscTree",
            "frame": "true",
            "linkedCheck": "true",
            "defaultFields": "({\n  _icon: { value: 'geejing' },\n  _checked: {\n    convert(value, data) {\n      if (value === true)\n        return true;\n      else\n        return data.text == 'Mexico' ? null : false;\n    }\n  }\n})"
          },
          "events": {
            "itemdblclick": "Wb.tip(item.data.text + ' row double clicked');"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "textCol",
                    "fieldName": "text",
                    "expander": "true",
                    "text": "Area",
                    "freeze": "true",
                    "width": "18em",
                    "render": "//use sub-color to render subText\nlet textEl, subText = data.percentage > 0.15 ? 'Large area' : null;\nif (subText) {\n  // Manual add sub text, please use subtext field instead\n  el.cls = 'w-text-wrap';\n  el.removeCls('w-text');\n  el.addEl('w-text').textContent = value;\n  el.addEl('w-sub-text').textContent = subText;\n  // let span = el.addTag('span');\n  // span.textContent = value;\n  // span = el.addEl('w-sub-color w-margin-l', 'span');\n  // span.textContent = subText;\n} else\n  return value;"
                  },
                  "text": "textCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "landAreasCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "landAreasCol",
                    "text": "Land Areas"
                  },
                  "_expanded": true,
                  "hasDupCid": 0,
                  "items": [
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "percentageCol",
                        "fieldName": "percentage",
                        "text": "Percentage",
                        "type": "percent",
                        "titleAlign": "right"
                      },
                      "text": "percentageCol",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 0
                    },
                    {
                      "cls": "Wb.Column",
                      "properties": {
                        "cid": "areaCol",
                        "fieldName": "area",
                        "text": "Area(Sq. Km)",
                        "titleAlign": "right",
                        "render": "if (value < 10000000) {\n  el.parentNode.cls = 'w-succ-bgcolor-row';\n  el.cls = 'w-error-color';\n  value = '* ' + value.intText;\n} else {\n  value = value?.intText;\n}\n\n//add icon manually\nlet wrapEl = el.addEl('w-row w-align-center');\nel.removeCls('w-text');\nwrapEl.addEl('w-item-icon w-icon icon-thumb');\nwrapEl.addEl('w-text').textContent = value;"
                      },
                      "text": "areaCol",
                      "_expanded": true,
                      "_icon": "column",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "remarkCol",
                    "fieldName": "remark",
                    "text": "Remark",
                    "width": "-1"
                  },
                  "text": "remarkCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "checkCol1",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol1",
                    "checkBox": "int",
                    "fieldName": "myCheck",
                    "checkRefresh": "true",
                    "menuText": "Check"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "checkCol2",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol2",
                    "checkBox": "roTrue",
                    "fieldName": "myCheck"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "checkCol3",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "checkCol3",
                    "checkBox": "roAll",
                    "fieldName": "myCheck"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "toggleCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "toggleCol",
                    "render": "let row = this;\nvalue = row.data.myCheck;\nrow.addFooter({\n  cname: 'toggle', returnType: 'int', value, toggleAlign: 'center', clickToggle: true,\n  events: {\n    change(value) {\n      // If there is no need to synchronously update other fields, this is more efficient\n      // row.data.myCheck = value; // only update myCheck field value\n      row.set('myCheck', value); // update whole row\n    }\n  }\n}, el);",
                    "width": "5em",
                    "text": "Toggle"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "linkCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "linkCol",
                    "render": "let a = el.addTag('a');\nel.cls = 'w-ta-center';\na.textContent = 'Download';\na.onclick = e => Wb.tip('Download data of ' + data.text);\n// a.onclick = app.myFunc;",
                    "width": "8em",
                    "text": "Link"
                  },
                  "hasDupCid": 0
                },
                {
                  "_icon": "column",
                  "text": "actionsCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "actionsCol",
                    "text": "Actions",
                    "width": "8em"
                  },
                  "hasDupCid": 0,
                  "items": [
                    {
                      "cls": "Wb.Array",
                      "properties": {
                        "cid": "buttons"
                      },
                      "text": "buttons",
                      "_expanded": true,
                      "_icon": "array",
                      "hasDupCid": 0,
                      "items": [
                        {
                          "_icon": "item",
                          "text": "setItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "setItem",
                            "icon": "gear",
                            "tip": "Set"
                          },
                          "events": {
                            "click": "Wb.tip('Set ' + this.data.text);"
                          },
                          "hasDupCid": 0
                        },
                        {
                          "_icon": "item",
                          "text": "shareItem",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "shareItem",
                            "icon": "share2",
                            "tip": "Add"
                          },
                          "events": {
                            "click": "Wb.tip('Share ' + this.data.text);"
                          },
                          "hasDupCid": 0
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "component",
          "text": "miscDescComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "miscDescComp",
            "html": "<div>Feature List:</div>\n<ul>\n  <li>Columns sortable, resizable, draggable and freezable.</li>\n  <li>Arbitrary multi-Level Headers.</li>\n  <li>Customize cell rendering.</li>\n  <li>Rows and columns highlighing.</li>\n  <li>Local data pagination.</li>\n  <li>Bind buttons or any controls to cells.</li>\n  <li>Multiple styles of check columns.</li>\n  <li>Customize columns and persistence to the database.</li>\n</ul>",
            "textSelectable": "false"
          }
        },
        {
          "_icon": "title",
          "text": "groupingTreeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "groupingTreeTitle",
            "title": "Grouping tree"
          }
        },
        {
          "_icon": "tree-view",
          "text": "groupingTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "groupingTree",
            "height": "20em",
            "resizable": "true",
            "gridLine": "true",
            "columnsSortable": "true",
            "sorters": "department",
            "frame": "true",
            "grouping": "true",
            "localData": "[\n  { name: 'Alice', department: 'Engineering', jobTitle: 'Software Engineer', country: 'USA', _leaf: true },\n  { name: 'Bob', department: 'Marketing', jobTitle: 'Marketing Manager', country: 'UK', _leaf: true },\n  { name: 'Charlie', department: 'Engineering', jobTitle: 'Junior Software Engineer', country: 'USA', _leaf: true },\n  { name: 'David', department: 'Sales', jobTitle: 'Sales Representative', country: 'China', _leaf: true },\n  { name: 'Eve', department: 'Marketing', jobTitle: 'Content Marketer', country: 'UK', _leaf: true },\n  { name: 'Frank', department: 'Engineering', jobTitle: 'Software Engineer', country: 'USA', _leaf: true },\n  { name: 'Grace', department: 'Sales', jobTitle: 'Sales Manager', country: 'China', _leaf: true },\n  { name: 'Heidi', department: 'Engineering', jobTitle: 'Junior Software Engineer', country: 'USA', _leaf: true },\n  { name: 'Ivan', department: 'Marketing', jobTitle: 'Social Media Marketer', country: 'UK', _leaf: true },\n  { name: 'Judy', department: 'Sales', jobTitle: 'Sales Representative', country: 'China', _leaf: true },\n  { name: 'Kevin', department: 'Engineering', jobTitle: 'Software Engineer', country: 'USA', _leaf: true },\n  { name: 'Luna', department: 'Marketing', jobTitle: 'Marketing Coordinator', country: 'UK', _leaf: true },\n  { name: 'Mike', department: 'Engineering', jobTitle: 'Junior Software Engineer', country: 'USA', _leaf: true },\n  { name: 'Nora', department: 'Sales', jobTitle: 'Sales Manager', country: 'China', _leaf: true },\n  { name: 'Oscar', department: 'Engineering', jobTitle: 'Intern', country: 'USA', _leaf: true }\n]"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "fieldName": "name",
                    "expander": "true",
                    "text": "Name",
                    "width": "18em",
                    "sortable": "false"
                  },
                  "text": "nameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "departmentCol",
                    "fieldName": "department",
                    "text": "Department",
                    "width": "18em"
                  },
                  "text": "departmentCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "jobTitleCol",
                    "fieldName": "jobTitle",
                    "text": "Job Title",
                    "width": "18em"
                  },
                  "text": "jobTitleCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "countryCol",
                    "fieldName": "country",
                    "text": "Country",
                    "width": "-1"
                  },
                  "text": "countryCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 0
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "editableTreeTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "editableTreeTitle",
            "title": "Editable tree"
          }
        },
        {
          "_icon": "tree-view",
          "text": "editableTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "editableTree",
            "columnsSortable": "true",
            "editable": "true",
            "height": "15em",
            "multiSelect": "true",
            "sorters": "area_name",
            "url": "demo-source?xaction=listAreaTree",
            "pagingBar": "true",
            "frame": "true"
          },
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "//Some related methods: tree.add()/tree.addData()/tree.insert()/tree.insertData()/tree.insertXXX()/tree.insertDataXXX() etc.\napp.editableTree.addRecord({ area_name: 'New item', _checked: false });"
                  }
                },
                {
                  "_icon": "item",
                  "text": "editBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "editBtn",
                    "text": "@Str.edit",
                    "icon": "edit",
                    "keys": "Ctrl+J"
                  },
                  "events": {
                    "click": "let firstRec = app.editableTree.firstItem;\nif (firstRec) {\n  firstRec.set({ sid: 'code', area_name: 'new name' });\n  app.editableTree.startEdit(firstRec, 'area_name');\n  firstRec.highlight();\n}"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "//Some related methods: tree.destroySelection()/item.destroy()/Wb.destroy(items);\napp.editableTree.delRecords();"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "item",
                  "text": "addChild",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addChild",
                    "text": "Add Child",
                    "icon": "insert"
                  },
                  "events": {
                    "click": "let tree = this.upBy(p => p instanceof Wb.Tree); //same as: app.editableTree\nlet firstNode = tree.items[0], newNode;\nfirstNode.expand(n => {\n  newNode = firstNode.addData({ area_name: 'new node', _leaf: true, _icon: 'earth', _checked: true });\n  //other relative methods: insertData, insertDataBefore, insertDataAfter, insert, add etc\n  newNode.select();\n});"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider2",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider2"
                  }
                },
                {
                  "_icon": "item",
                  "text": "saveBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "saveBtn",
                    "text": "@Str.save",
                    "icon": "save",
                    "keys": "Ctrl+S"
                  },
                  "events": {
                    "click": "app.editableTree.sync({\n  url: xpath, //Should be changed to a valid URL\n  success() {\n    Wb.tipDone();\n  }\n});"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "areaNameCol",
                    "text": "Area name",
                    "fieldName": "area_name",
                    "expander": "true",
                    "width": "-1"
                  },
                  "text": "areaNameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "sidCol",
                    "text": "Id",
                    "fieldName": "sid",
                    "width": "15em"
                  },
                  "text": "sidCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "parentIdCol",
                    "text": "Parent id",
                    "fieldName": "parent_id",
                    "width": "15em"
                  },
                  "text": "parentIdCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "combo",
                      "text": "editor",
                      "cls": "Wb.Select",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "forceSelect": "true",
                        "url": "demo-source?xaction=areaSearch",
                        "textField": "sid",
                        "required": "true"
                      },
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: view.xwl


{
  "title": "",
  "icon": "list-view",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/css/demo.css\"\n]",
    "description": "demo-source=example/common/demo-source.xwl"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Array} - Common view data. */\n  viewData: [\n    { icon: 'user', text: 'User Management' },\n    { icon: 'support', text: 'Support Service' },\n    { icon: 'list', text: 'Consumption List' },\n    { icon: 'preview', text: 'Disabled Item', _disabled: true, _tip: 'This item is disabled' },\n    { icon: 'gear', text: 'System Settings' },\n    { icon: 'truck', text: 'The text content of this item is too long and maybe overflowed' },\n    { icon: 'database', text: 'Database Management' },\n    { icon: 'flow1', text: 'WorkFlow Design' }\n  ]\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "View",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "commonViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "commonViewTitle",
            "title": "Common view"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "commonItemTplViewLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "commonItemTplViewLabel",
            "text": "Define the content of ViewItem by defining itemTpl."
          }
        },
        {
          "_icon": "list-view",
          "text": "commonItemTplView",
          "cls": "Wb.View",
          "properties": {
            "cid": "commonItemTplView",
            "itemTpl": "<div class=\"w-row w-align-center w-gapd5 w-padding\">\n  <div class=\"w-icon icon-{icon}\"></div>\n  <div class=\"w-name\">{text}</div>\n</div>",
            "layout": "column",
            "data": "app.viewData",
            "height": "15em"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "commonItemTplFnViewLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "commonItemTplFnViewLabel",
            "text": "Define the content of ViewItem by defining itemTplFn."
          }
        },
        {
          "_icon": "list-view",
          "text": "commonItemTplFnView",
          "cls": "Wb.View",
          "properties": {
            "cid": "commonItemTplFnView",
            "layout": "column",
            "data": "app.viewData",
            "height": "15em",
            "frame": "true",
            "itemTplFn": "//Warning: [Wb.toHTML] must be used to convert value to HTML to avoid potential XSS security risks\n\nreturn `<div class=\"w-row w-align-center w-gapd5 w-padding\">\n  <div class=\"w-icon icon-${Wb.toHTML(data.icon)}\"></div>\n  <div class=\"w-name\">${Wb.toHTML(data.text)}</div>\n</div>`;"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "customItemViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "customItemViewTitle",
            "title": "Customized item itself"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "customItemViewLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "customItemViewLabel",
            "text": "Define ViewItem itself by identifying the \"w-item\" class in itemTpl."
          }
        },
        {
          "_icon": "label",
          "text": "fixed4ColumnsLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "fixed4ColumnsLabel",
            "text": "Fixed 4 columns"
          }
        },
        {
          "_icon": "list-view",
          "text": "fixed4ColumnsView",
          "cls": "Wb.View",
          "properties": {
            "cid": "fixed4ColumnsView",
            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-label w-ta-center\">{text}</div>\n</div>",
            "data": "app.viewData",
            "resizable": "true",
            "frame": "true",
            "bodyCls": "w-grid4 w-padding w-gapd5"
          }
        },
        {
          "_icon": "label",
          "text": "autoWrapItemsLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "autoWrapItemsLabel",
            "text": "Auto wrap items with fixed item width"
          },
          "_expanded": true
        },
        {
          "_icon": "list-view",
          "text": "autoWrapView",
          "cls": "Wb.View",
          "properties": {
            "cid": "autoWrapView",
            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5\" style=\"width:10em\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-name w-ta-center w-aligns-stretch\">{text}</div>\n</div>",
            "data": "app.viewData",
            "resizable": "true",
            "frame": "true",
            "bodyCls": "w-form w-padding w-gapd5"
          }
        },
        {
          "_icon": "label",
          "text": "noWrapItemsLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "noWrapItemsLabel",
            "text": "No wrap items with fixed item width and stick selection"
          },
          "_expanded": true
        },
        {
          "_icon": "list-view",
          "text": "noWrapView",
          "cls": "Wb.View",
          "properties": {
            "cid": "noWrapView",
            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5 w-no-shrink\" style=\"width:10em\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-name w-ta-center w-aligns-stretch\">{text}</div>\n</div>",
            "data": "app.viewData",
            "multiSelect": "true",
            "resizable": "true",
            "frame": "true",
            "bodyCls": "w-row w-padding w-gapd5 w-visible-all",
            "stickSelect": "true"
          }
        },
        {
          "_icon": "title",
          "text": "variousTemplateSyntaxTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "variousTemplateSyntaxTitle",
            "title": "Various template syntax"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "useItemTplLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "useItemTplLabel",
            "text": "Use itemTpl"
          }
        },
        {
          "_icon": "list-view",
          "text": "variousItemTplSyntaxView",
          "cls": "Wb.View",
          "properties": {
            "cid": "variousItemTplSyntaxView",
            "itemTpl": "<!-- Tip: For complex templates, it is recommended to use itemTplFn to define custom function -->\n<div class=\"w-item w-border\" style=\"display:grid;grid-template-columns:2em 8em 16em 1fr;padding:1em;gap:.5em\">\n  <div>{num}</div>\n  <div>{[data.money.usd]}</div>\n  <div>{[data.date.dateTimeText12]}</div>\n  <div>{for data.array}{if index > 0}<span>, </span>{/if}<span>{[index + 1]}: </span><span>{[value]}</span>{/for}</div>\n</div>",
            "layout": "column",
            "data": "[\n  { num: 1, money: 581234.56, date: new Date(), array: ['item1', 'item2', 'item3'] },\n  { num: 2, money: 324549.7, date: new Date(), array: ['item4', 'item5', 'item6'] },\n  { num: 3, money: 53543.28, date: new Date(), array: ['item7', 'item8', 'item9'] }\n]",
            "resizable": "true",
            "frame": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "useItemTplFnLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "useItemTplFnLabel",
            "text": "Use itemTplFn with fixed header"
          }
        },
        {
          "_icon": "list-view",
          "text": "useItemTplFnView",
          "cls": "Wb.View",
          "properties": {
            "cid": "useItemTplFnView",
            "layout": "column",
            "data": "[\n  { num: 1, money: 581234.56, date: new Date(), array: ['item1', 'item2', 'item3'] },\n  { num: 2, money: 324549.7, date: new Date(), array: ['item4', 'item5', 'item6'] },\n  { num: 3, money: 53543.28, date: new Date(), array: ['item7', 'item8', 'item9'] }\n]",
            "resizable": "true",
            "frame": "true",
            "itemTplFn": "//Warning: [Wb.toHTML] must be used to convert value to HTML to avoid potential XSS security risks\n\nlet arrayText = '';\ndata.array.forEach((item, i) => {\n  if (i > 0)\n    arrayText += '<span>, </span>';\n  arrayText += '<span>' + (i + 1) + ': </span><span>' + Wb.toHTML(item) + '</span>';\n});\nreturn `<div class=\"w-item w-border\" style=\"display:grid;grid-template-columns:2em 8em 16em 1fr;padding:1em;gap:.5em\">\n  <div>${Wb.toHTML(data.num)}</div>\n  <div>${Wb.toHTML(data.money.usd)}</div>\n  <div>${Wb.toHTML(data.date.dateTimeText12)}</div>\n  <div>${arrayText}</div>\n</div>`;",
            "html": "<div class=\"w-fixed-bgcolor\" style=\"display:grid;grid-template-columns:2em 8em 16em 1fr;padding:1em;gap:.5em\">\n  <div></div>\n  <div>Money</div>\n  <div>Date</div>\n  <div>Content</div>\n</div>\n<div class=\"w-items w-flex\">\n</div>",
            "height": "12em"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "nameListTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "nameListTitle",
            "title": "Name list with remote data"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "nameListLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "nameListLabel",
            "text": "A common way to display a list of names with remote data and remote pagination."
          }
        },
        {
          "_icon": "list-view",
          "text": "nameListView",
          "cls": "Wb.View",
          "properties": {
            "cid": "nameListView",
            "itemTpl": "<div class=\"w-item w-border w-row w-padding w-align-center w-gapd5\">\n  <div class=\"w-icon icon-user2 w-size2\"></div>\n  <div class=\"w-column w-gapd2\">\n    <div class=\"w-name\">{full_name}</div>\n    <div class=\"w-name w-sub-color w-sized8\">{code}</div>\n  </div>\n  <div class=\"w-flex\"></div>\n  <div class=\"w-column w-sized8 w-gapd2\">\n    <div class=\"w-name\">{[new Date().format('HH:mm')]}</div>\n    <div class=\"w-icon icon-flag4 w-sub-color w-aligns-end\"></div>\n  </div>\n</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDict",
            "height": "20em",
            "pagingBar": "true",
            "pageSize": "10"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "tagListTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tagListTitle",
            "title": "Tag style list"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "tagListLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "tagListLabel",
            "text": "Display a list of tags with customized css and closed button."
          }
        },
        {
          "_icon": "list-view",
          "text": "tagListView",
          "cls": "Wb.View",
          "properties": {
            "cid": "tagListView",
            "itemTpl": "<div class=\"w-item w-row w-padding w-align-center w-gapd5\">\n  <div class=\"w-icon icon-tag\"></div>\n  <div class=\"w-name\">{full_name}</div>\n  <div class=\"w-btn w-close-btn w-btn-light\">\n    <div class=\"w-icon icon-close\"></div>\n  </div>\n</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDict",
            "height": "15em",
            "bodyCls": "w-form w-padding w-gapd5",
            "cls": "d-tag",
            "multiSelect": "true",
            "rectSelect": "true",
            "captureClass": ".w-close-btn"
          },
          "_expanded": true,
          "events": {
            "captureclick": "item.destroy();"
          }
        },
        {
          "_icon": "title",
          "text": "pagingTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "pagingTitle",
            "title": "Pagination display"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "remoteDataPagingLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "remoteDataPagingLabel",
            "text": "Remote data pagination"
          }
        },
        {
          "_icon": "list-view",
          "text": "remotePagingView",
          "cls": "Wb.View",
          "properties": {
            "cid": "remotePagingView",
            "itemTpl": "<div class=\"w-padding\">{code} - {full_name}</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDict",
            "height": "20em",
            "pagingBar": "pageNums",
            "pageSize": "10"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "cachedPagingLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "cachedPagingLabel",
            "text": "Caching remote data and paging locally."
          }
        },
        {
          "_icon": "list-view",
          "text": "cachedPagingView",
          "cls": "Wb.View",
          "properties": {
            "cid": "cachedPagingView",
            "itemTpl": "<div class=\"w-padding\">{code} - {full_name}</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDict",
            "height": "20em",
            "pagingBar": "pageNums",
            "pageSize": "10",
            "cached": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "localDataPagingLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "localDataPagingLabel",
            "text": "Local data pagination"
          }
        },
        {
          "_icon": "list-view",
          "text": "localDataPagingView",
          "cls": "Wb.View",
          "properties": {
            "cid": "localDataPagingView",
            "layout": "column",
            "resizable": "true",
            "height": "15em",
            "frame": "true",
            "localData": "[\n  { text: 'item1' }, { text: 'item2' }, { text: 'item3' }, { text: 'item4' }, { text: 'item5' },\n  { text: 'item6' }, { text: 'item7' }, { text: 'item8' }, { text: 'item9' }, { text: 'item10' },\n  { text: 'item11' }, { text: 'item12' }, { text: 'item13' }, { text: 'item14' }, { text: 'item15' },\n  { text: 'item16' }, { text: 'item17' }, { text: 'item18' }, { text: 'item19' }\n]",
            "pagingBar": "true",
            "pageSize": "10",
            "itemTpl": "<div class=\"w-padding\">{text}</div>"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "bottomNextPageLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "bottomNextPageLabel",
            "text": "Auto load next page when scroll to the bottom"
          }
        },
        {
          "_icon": "list-view",
          "text": "bottomNextPageView",
          "cls": "Wb.View",
          "properties": {
            "cid": "bottomNextPageView",
            "itemTpl": "<div class=\"w-item w-padding1\">{code} - {full_name}</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDict",
            "height": "20em",
            "pageSize": "10",
            "autoPaging": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "topNextPageLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "topNextPageLabel",
            "text": "Auto load next page when scroll to the top"
          }
        },
        {
          "_icon": "list-view",
          "text": "topNextPageView",
          "cls": "Wb.View",
          "properties": {
            "cid": "topNextPageView",
            "itemTpl": "<div class=\"w-item w-padding1\">{code} - {full_name}</div>",
            "resizable": "true",
            "frame": "true",
            "url": "demo-source?xaction=staffDictDesc",
            "height": "20em",
            "pageSize": "10",
            "autoPaging": "false",
            "reverseData": "true",
            "autoToBottom": "true"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "miscViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "miscViewTitle",
            "title": "Miscillaneous view"
          },
          "_expanded": true
        },
        {
          "_icon": "list-view",
          "text": "miscView",
          "cls": "Wb.View",
          "properties": {
            "cid": "miscView",
            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-label w-ta-center\">{text}</div>\n</div>",
            "layout": "grid4",
            "data": "app.viewData",
            "multiSelect": "true",
            "rectSelect": "true",
            "resizable": "true",
            "frame": "true",
            "keyWalking": "true",
            "bodyCls": "w-grid4",
            "padding": "2em",
            "gap": "2em",
            "reserveScrollbar": "true",
            "draggable": "true",
            "droppable": "demo.miscView"
          },
          "events": {
            "itemdrop": "Wb.tip('Drop items: ' + draggable.dropItems.length + '\\nDest: ' + draggable.dest.data.text + '\\nMode: ' + draggable.mode);"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true",
                "gap": ".5em"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "selectItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "selectItem",
                    "text": "Select"
                  },
                  "events": {
                    "click": "let miscView = app.miscView;\nmiscView.selection = [miscView.items[0], miscView.items[1]];\n// miscView.items[2].selected = true;"
                  }
                },
                {
                  "_icon": "item",
                  "text": "deselectItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "deselectItem",
                    "text": "Deselect"
                  },
                  "events": {
                    "click": "app.miscView.deselectAll();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "addItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addItem",
                    "text": "Add"
                  },
                  "events": {
                    "click": "let item = app.miscView.addData({ text: 'New Item', icon: 'earth' });\nitem.selected = true;\nitem.highlight();\n// app.miscView.add({ data: { text: 'New Item', icon: 'earth' } }); //equals above"
                  }
                },
                {
                  "_icon": "item",
                  "text": "insertItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "insertItem",
                    "text": "Insert"
                  },
                  "events": {
                    "click": "let item = app.miscView.insertData(1, { text: 'Insert Item', icon: 'record' });\nitem.selected = true;\nitem.highlight();\n// app.miscView.insert(1, { data: { text: 'Insert Item', icon: 'record' } }); //equals above"
                  }
                },
                {
                  "_icon": "item",
                  "text": "deleteItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "deleteItem",
                    "text": "Delete"
                  },
                  "events": {
                    "click": "app.miscView.firstItem?.destroy();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "deleteSelectionsItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "deleteSelectionsItem",
                    "text": "Delete selections"
                  },
                  "events": {
                    "click": "let view = app.miscView;\n\nif (!view.selections.length) {\n  Wb.tipSelect();\n  return;\n}\nview.destroySelections();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "updateItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "updateItem",
                    "text": "Update"
                  },
                  "events": {
                    "click": "let item = app.miscView.items[0];\n\nif (item) {\n  item.set('text', 'Updated'); // equals to: item.proxy.text='Updated';\n  item.set('icon', 'flower'); // equals to: item.proxy.icon='flower';\n  item.focus();\n  item.highlight();\n  // item.update({ text: 'Updated', icon: 'flower' }); // More effective when updating multiple properties\n  // item.data = { text: 'Updated', icon: 'flower' }; // Update all data\n}"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "component",
          "text": "miscDescComp",
          "cls": "Wb.Component",
          "properties": {
            "cid": "miscDescComp",
            "html": "<div>Feature List:</div>\n<ul>\n  <li>Drag to draw a rectangle to select items.</li>\n  <li>Press key to enter name in the view can navigate to the specified item.</li>\n  <li>Maintain scrollbar position after reloading data.</li>\n  <li>Intelligent navigate to specified item based on arrow key direction.</li>\n  <li>Drag-and-Drop support.</li>\n</ul>",
            "textSelectable": "false"
          }
        },
        {
          "_icon": "title",
          "text": "listViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "listViewTitle",
            "title": "List view"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "listViewLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "listViewLabel",
            "text": "List items in the specified pattern"
          }
        },
        {
          "_icon": "list1",
          "text": "listView",
          "cls": "Wb.ListView",
          "properties": {
            "cid": "listView",
            "height": "20em",
            "url": "demo-source?xaction=staffDict",
            "textField": "full_name",
            "subtextField": "code",
            "defaultFields": "({\n  _icon: { value: 'user2' },\n  badge: { value: 8 },\n  iconvalue: { value: 'bell-off' },\n  datevalue: { value: new Date() }\n})",
            "resizable": "true"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "events": {
                "buttonclick": "app.listView.viewType = button.cid;"
              },
              "properties": {
                "cid": "tbar",
                "isProperty": "true",
                "gap": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "smallList",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "smallList",
                    "text": "Small List",
                    "groupName": "viewType",
                    "active": "true"
                  }
                },
                {
                  "_icon": "item",
                  "text": "list",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "list",
                    "text": "List",
                    "groupName": "viewType",
                    "active": "true"
                  }
                },
                {
                  "_icon": "item",
                  "text": "smallIcon",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "smallIcon",
                    "text": "Small Icon",
                    "groupName": "viewType"
                  }
                },
                {
                  "_icon": "item",
                  "text": "mediumIcon",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "mediumIcon",
                    "text": "Medium Icon",
                    "groupName": "viewType"
                  }
                },
                {
                  "_icon": "item",
                  "text": "bigIcon",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "bigIcon",
                    "text": "Big Icon",
                    "groupName": "viewType"
                  }
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "iconViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "iconViewTitle",
            "title": "Icon view"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "iconViewLabel",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "iconViewLabel",
            "text": "Display items with a big icon and text label, set layout to \"grid4\" to display 4 fixed columns, set \"padding\" and \"gap\" to adjust space."
          }
        },
        {
          "_icon": "icon-list",
          "text": "iconView",
          "cls": "Wb.IconView",
          "properties": {
            "cid": "iconView",
            "data": "app.viewData",
            "iconField": "icon",
            "resizable": "true",
            "selectColor": "active",
            "defaultFields": "({\n  badge: { value: 99 }\n})",
            "layout": "grid4",
            "padding": ".5em",
            "gap": "1em"
          },
          "events": {
            "itemclick": "Wb.tip(item.data.text + ' clicked');"
          }
        },
        {
          "_icon": "title",
          "text": "checkViewTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "checkViewTitle",
            "title": "Check view"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "checkViewLabel1",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "checkViewLabel1",
            "text": "Display items for single or multiple check"
          }
        },
        {
          "_icon": "check7",
          "text": "checkView1",
          "cls": "Wb.CheckView",
          "properties": {
            "cid": "checkView1",
            "data": "[\n  { text: 'Chrome', value: 1, _icon: 'chrome' },\n  { text: 'Edge', value: 2, _icon: 'edge', _selected: true },\n  { text: 'Firefox', value: 3, _icon: 'firefox' },\n  { text: 'Safari', value: 4, _icon: 'safari' },\n  { text: 'Image icon', value: 5, _img: 'wb/images/chart.png' }\n]",
            "multiSelect": "true",
            "maxWidth": "15em"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true",
                "gap": ".5em"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "setValueItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "setValueItem",
                    "text": "Set Value"
                  },
                  "events": {
                    "click": "app.checkView1.value = [2, 3];"
                  }
                },
                {
                  "_icon": "item",
                  "text": "getValueItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "getValueItem",
                    "text": "Get Value"
                  },
                  "events": {
                    "click": "Wb.tip('The value is: ' + Wb.encode(app.checkView1.value));"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: visual.xwl


{
  "title": "",
  "icon": "image",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Image, Video and Icon",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "imageVideoIconListTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "imageVideoIconListTitle",
            "title": "Image, video and icon list"
          }
        },
        {
          "_icon": "container",
          "text": "imageVideoIconListCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "imageVideoIconListCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "label",
              "text": "commonImageLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonImageLabel",
                "text": "Common image with click preview feature"
              }
            },
            {
              "_icon": "image",
              "text": "commonImage",
              "cls": "Wb.Image",
              "properties": {
                "cid": "commonImage",
                "src": "wb/images/app/logo.png",
                "height": "6em",
                "clickPreview": "true",
                "width": "6em"
              }
            },
            {
              "_icon": "label",
              "text": "commonPictureLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonPictureLabel",
                "text": "Common picture with click preview feature"
              }
            },
            {
              "_icon": "picture",
              "text": "commonPicture",
              "cls": "Wb.Picture",
              "properties": {
                "cid": "commonPicture",
                "src": "wb/images/app/logo.png",
                "clickPreview": "true"
              }
            },
            {
              "_icon": "label",
              "text": "originalSizeLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "originalSizeLabel",
                "text": "Original size image"
              }
            },
            {
              "_icon": "image",
              "text": "originalSizeImage",
              "cls": "Wb.Image",
              "properties": {
                "cid": "originalSizeImage",
                "src": "wb/images/support.png",
                "size": "auto",
                "width": "6em",
                "height": "5em"
              }
            },
            {
              "_icon": "label",
              "text": "videoLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "videoLabel",
                "text": "Video"
              }
            },
            {
              "_icon": "video",
              "text": "demoVideo",
              "cls": "Wb.Video",
              "properties": {
                "cid": "demoVideo",
                "src": "https://59b5fe18-b640-4ed9-a7d0-5867d0563fe4.mdnplay.dev/shared-assets/videos/flower.mp4",
                "height": "20em",
                "controls": "true"
              }
            },
            {
              "_icon": "label",
              "text": "commonIconLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonIconLabel",
                "text": "Common icon"
              }
            },
            {
              "_icon": "logo",
              "text": "commonIcon",
              "cls": "Wb.Icon",
              "properties": {
                "cid": "commonIcon",
                "icon": "gear"
              },
              "_expanded": true
            },
            {
              "_icon": "label",
              "text": "commonImageIconLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "commonImageIconLabel",
                "text": "Image icon"
              }
            },
            {
              "_icon": "logo",
              "text": "commonImageIcon",
              "cls": "Wb.Icon",
              "properties": {
                "cid": "commonImageIcon",
                "img": "support"
              },
              "_expanded": true
            },
            {
              "_icon": "label",
              "text": "anySizeIconLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "anySizeIconLabel",
                "text": "Any size icon, image and char Icon"
              }
            },
            {
              "_icon": "container",
              "text": "anySizeCt",
              "cls": "Wb.Container",
              "properties": {
                "cid": "anySizeCt",
                "layout": "form"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "logo",
                  "text": "icon1",
                  "cls": "Wb.Icon",
                  "_expanded": true,
                  "properties": {
                    "cid": "icon1",
                    "icon": "flower"
                  }
                },
                {
                  "_icon": "logo",
                  "text": "icon2",
                  "cls": "Wb.Icon",
                  "_expanded": true,
                  "properties": {
                    "cid": "icon2",
                    "fontSize": "1.2em",
                    "img": "field"
                  }
                },
                {
                  "_icon": "logo",
                  "text": "icon3",
                  "cls": "Wb.Icon",
                  "_expanded": false,
                  "properties": {
                    "cid": "icon3",
                    "fontSize": "1.5em",
                    "icon": "🏥"
                  }
                },
                {
                  "_icon": "logo",
                  "text": "icon4",
                  "cls": "Wb.Icon",
                  "_expanded": false,
                  "properties": {
                    "cid": "icon4",
                    "fontSize": "2em",
                    "icon": "list"
                  }
                },
                {
                  "_icon": "logo",
                  "text": "icon5",
                  "cls": "Wb.Icon",
                  "_expanded": false,
                  "properties": {
                    "cid": "icon5",
                    "fontSize": "3em",
                    "icon": "🥋"
                  }
                },
                {
                  "_icon": "logo",
                  "text": "icon6",
                  "cls": "Wb.Icon",
                  "_expanded": false,
                  "properties": {
                    "cid": "icon6",
                    "fontSize": "4em",
                    "icon": "🎭"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Send text to \"wb.ws.commonSocket\" of current user.\n   */\n  sendTextToCurrentUser() {\n    Wb.send(Params.text, 'wb.ws.commonSocket');\n  },\n  /**\n   * Send binary to \"wb.ws.binarySocket\" of current user.\n   */\n  sendBinaryToCurrentUser() {\n    // Caution: if use Wb.send(inputStream, 'wb.ws.binarySocket') to send InputStream,\n    // only the first websocket client will receive data, because the InputStream is unidirectional\n\n    let bytes = new Wb.File(true, 'wb/images/app/logo.png').bytes;\n    Wb.send(bytes, 'wb.ws.binarySocket');\n  },\n  /**\n   * Send text to \"wb.ws.commonSocket\" of the specified user.\n   */\n  sendTextToSpecifiedUser() {\n    Wb.send(Params.text, 'wb.ws.commonSocket', 'admin');\n  },\n  /**\n   * Broadcast JSON to \"wb.ws.commonSocket\" of all users.\n   */\n  broadcastJSONToSpecifiedUser() {\n    //use Wb.serialize/Wb.deserialize to send object\n    Wb.send({ text: 'text message', number: 123 }, 'wb.ws.commonSocket', true);\n  },\n  /**\n   * Receive data from client web socket.\n   */\n  receiveData() {\n    //Global parameters:\n    //data: data sent by the client websocket\n    //session: websocket session\n    //httpSession: http session\n    let value;\n\n    if (data instanceof InputStream)\n      value = IOUtils.toString(data); // data is ByteArrayInputStream, no need to close\n    else\n      value = data;\n    Wb.log('Received: ' + value);\n    Wb.send('result'); // send text to the client web socket\n    // Wb.send(inputStream); // send inputStream to the client web socket\n    // Wb.send(bytes); // send bytes to the client web socket\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: web-socket.xwl


{
  "title": "",
  "icon": "plug",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "plug",
      "text": "commonSocket",
      "cls": "Wb.Socket",
      "properties": {
        "cid": "commonSocket",
        "name": "wb.ws.commonSocket"
      },
      "events": {
        "message": "// let object = Wb.decode(e.data); // if e.data is JSON text\nWb.tip('Received: ' + e.data);"
      }
    },
    {
      "_icon": "plug",
      "text": "xwlSocket",
      "cls": "Wb.Socket",
      "_expanded": true,
      "properties": {
        "cid": "xwlSocket",
        "name": "wb.ws.xwlSocket",
        "xwl": "@xpath + \"/actions {xaction: 'receiveData'}\""
      },
      "events": {
        "message": "Wb.tip('Received: ' + e.data);"
      }
    },
    {
      "_icon": "plug",
      "text": "binarySocket",
      "cls": "Wb.Socket",
      "_expanded": false,
      "properties": {
        "cid": "binarySocket",
        "name": "wb.ws.binarySocket"
      },
      "events": {
        "message": "let blob = e.data;\nWb.readBlob(blob, value => {\n  Wb.tip('<img src=\"data:image/png;base64,' + value + '\">', true);\n});"
      }
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "WebSocket",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "label",
          "text": "sendTextToCurrentUserLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sendTextToCurrentUserLabel",
            "text": "Send text to the specified web socket of current user"
          }
        },
        {
          "_icon": "button",
          "text": "sendTextToCurrentUserBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "sendTextToCurrentUserBtn",
            "text": "Send text to current user",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=sendTextToCurrentUser',\n  params: { text: 'text message' }\n});"
          }
        },
        {
          "_icon": "label",
          "text": "sendBinaryToCurrentUserLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sendBinaryToCurrentUserLabel",
            "text": "Send binary to the specified web socket of current user"
          }
        },
        {
          "_icon": "button",
          "text": "sendBinaryToCurrentUserBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "sendBinaryToCurrentUserBtn",
            "text": "Send binary to current user",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=sendBinaryToCurrentUser'\n});"
          }
        },
        {
          "_icon": "label",
          "text": "sendTextToSpecifyUserLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sendTextToSpecifyUserLabel",
            "text": "Send text to the specified web socket of the specified user"
          }
        },
        {
          "_icon": "button",
          "text": "sendTextToSpecifiedUserBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "sendTextToSpecifiedUserBtn",
            "text": "Send text to \"admin\" user",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=sendTextToSpecifiedUser',\n  params: { text: 'text message' }\n});"
          }
        },
        {
          "_icon": "label",
          "text": "broadcastJsonToAllUsersLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "broadcastJsonToAllUsersLabel",
            "text": "Broadcast JSON to the specified web socket of all users"
          }
        },
        {
          "_icon": "button",
          "text": "broadcastJsonToAllUsersBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "broadcastJsonToAllUsersBtn",
            "text": "Broadcast JSON to all users",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=broadcastJSONToSpecifiedUser'\n});"
          }
        },
        {
          "_icon": "label",
          "text": "sendTextBySocketLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sendTextBySocketLabel",
            "text": "Send text to the specified xwl module by xwlSocket"
          }
        },
        {
          "_icon": "button",
          "text": "sendTextBySocketBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "sendTextBySocketBtn",
            "text": "Send text by socket",
            "type": "primary"
          },
          "events": {
            "click": "app.xwlSocket.send('text');"
          }
        },
        {
          "_icon": "label",
          "text": "sendBinaryBySocketLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "sendBinaryBySocketLabel",
            "text": "Send binary to the specified xwl module by xwlSocket"
          }
        },
        {
          "_icon": "button",
          "text": "sendBinaryBySocketBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "sendBinaryBySocketBtn",
            "text": "Send binary by socket",
            "type": "primary"
          },
          "events": {
            "click": "let blob = new Blob([Wb.encode({ foo: 'bar' })], { type: 'application/json' });\napp.xwlSocket.send(blob);"
          }
        }
      ]
    }
  ]
}

File name: window.xwl


{
  "title": "",
  "icon": "window",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  //Define Share tip\n  myShareTip: new Wb.Tip({\n    cls: 'w-error-color', fontSize: '1.5em', padding: '.2em', events: {\n      beforetip(comp) {\n        this.text = 'My share tip: ' + comp.cid;\n      }\n    }\n  })\n});"
  },
  "items": [
    {
      "_icon": "array",
      "text": "no instanced items",
      "cls": "Wb.Array",
      "properties": {
        "cid": "no instanced items"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "window",
          "text": "dynamicNewWin",
          "cls": "Wb.Window",
          "_expanded": false,
          "properties": {
            "cid": "dynamicNewWin",
            "instanced": "false",
            "layout": "grid1",
            "closeAction": "destroy",
            "title": "Dynamic window"
          },
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Your name"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "calendar",
              "text": "date1",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date1",
                "text": "Birth date"
              },
              "_expanded": true,
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "window",
          "text": "modalWin",
          "cls": "Wb.Window",
          "_expanded": false,
          "properties": {
            "cid": "modalWin",
            "modal": "true",
            "icon": "bluetooth1",
            "title": "Modal window",
            "layout": "grid1"
          },
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Your name"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "calendar",
              "text": "date1",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date1",
                "text": "Birth date"
              },
              "_expanded": true,
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "window",
          "text": "autoResetWin",
          "cls": "Wb.Window",
          "_expanded": false,
          "properties": {
            "cid": "autoResetWin",
            "icon": "bluetooth1",
            "title": "Auto reset window",
            "layout": "grid1",
            "resetDialog": "true"
          },
          "items": [
            {
              "_icon": "label",
              "text": "label1",
              "cls": "Wb.Label",
              "properties": {
                "cid": "label1",
                "text": "The values of all controls will be automatically reset after the window is hidden"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Your name"
              },
              "_expanded": true,
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text2",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text2",
                "value": "abc",
                "text": "Default value"
              }
            },
            {
              "_icon": "calendar",
              "text": "date1",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date1",
                "text": "Birth date"
              },
              "_expanded": true,
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "dialog",
          "text": "normalDialog",
          "cls": "Wb.Dialog",
          "_expanded": true,
          "properties": {
            "cid": "normalDialog",
            "title": "Normal Dialog",
            "layout": "grid1"
          }
        },
        {
          "_icon": "dialog",
          "text": "customDialog",
          "cls": "Wb.Dialog",
          "_expanded": false,
          "properties": {
            "cid": "customDialog",
            "title": "Custom Dialog",
            "layout": "grid1",
            "plainTitle": "false",
            "labelUp": "true",
            "titleCenter": "false"
          },
          "items": [
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Your name"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "calendar",
              "text": "date1",
              "cls": "Wb.Date",
              "properties": {
                "cid": "date1",
                "text": "Birth date"
              },
              "_expanded": true,
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "newDialog",
              "cls": "Wb.Button",
              "properties": {
                "cid": "newDialog",
                "text": "New Dialog",
                "type": "primary"
              },
              "events": {
                "click": "app.newDialog.show();"
              },
              "hasDupCid": 1
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "tools"
              },
              "text": "tools",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Item",
                  "events": {
                    "click": "Wb.tip(Str.set);"
                  },
                  "properties": {
                    "cid": "item1",
                    "icon": "gear"
                  },
                  "text": "item1",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                }
              ]
            },
            {
              "cls": "Wb.Menu",
              "properties": {
                "cid": "menu",
                "isProperty": "true"
              },
              "text": "menu",
              "_expanded": true,
              "_icon": "menu2",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "add",
                    "text": "@Str.add"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "edit",
                    "text": "@Str.edit"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "item3",
                    "icon": "delete",
                    "text": "@Str.del"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "dialog",
          "text": "newDialog",
          "cls": "Wb.Dialog",
          "_expanded": true,
          "properties": {
            "cid": "newDialog",
            "title": "New Dialog",
            "layout": "grid1"
          },
          "hasDupCid": 1
        },
        {
          "_icon": "drawer",
          "text": "defaultDrawer",
          "cls": "Wb.Drawer",
          "properties": {
            "cid": "defaultDrawer",
            "layout": "fit",
            "icon": "fieldset",
            "title": "Default Drawer",
            "closable": "true",
            "focusControl": "false"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "toolbar",
              "text": "toolbar1",
              "cls": "Wb.Toolbar",
              "_expanded": true,
              "properties": {
                "cid": "toolbar1",
                "layout": "grid5",
                "vertical": "true"
              },
              "hasDupCid": 1,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "add",
                    "text": "Item1",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "edit",
                    "text": "Item2",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item3",
                    "icon": "delete",
                    "text": "Item3",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item4",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item4",
                    "icon": "book1",
                    "text": "Item4",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item5",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item5",
                    "icon": "discovery",
                    "text": "Item5",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item6",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item6",
                    "icon": "comment2",
                    "text": "Item6",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item7",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item7",
                    "icon": "flow1",
                    "text": "Item7",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item8",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item8",
                    "icon": "dict",
                    "text": "Item8",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  }
                },
                {
                  "_icon": "item",
                  "text": "item9",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item9",
                    "icon": "grid",
                    "text": "Item9",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  }
                },
                {
                  "_icon": "item",
                  "text": "item10",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item10",
                    "icon": "layout",
                    "text": "Item10",
                    "iconAlign": "top",
                    "iconSize": "2em"
                  }
                },
                {
                  "_icon": "line",
                  "text": "line1",
                  "cls": "Wb.Line",
                  "properties": {
                    "cid": "line1",
                    "title": "My Title"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "Input text",
                    "gridColumn": "span 5"
                  },
                  "_expanded": true,
                  "hasDupCid": 1
                },
                {
                  "_icon": "switcher-on",
                  "text": "toggle1",
                  "cls": "Wb.Toggle",
                  "properties": {
                    "cid": "toggle1",
                    "text": "Toggle",
                    "gridColumn": "span 5"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "tools"
              },
              "text": "tools",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Item",
                  "properties": {
                    "cid": "item1",
                    "icon": "gear"
                  },
                  "text": "item1",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                },
                {
                  "cls": "Wb.Item",
                  "properties": {
                    "cid": "item2",
                    "icon": "share"
                  },
                  "text": "item2",
                  "_expanded": true,
                  "_icon": "item",
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "drawer",
          "text": "rightDrawer",
          "cls": "Wb.Drawer",
          "properties": {
            "cid": "rightDrawer",
            "layout": "fit",
            "title": "Right Drawer",
            "dockPosition": "right"
          },
          "_expanded": false,
          "items": [
            {
              "_icon": "toolbar",
              "text": "toolbar1",
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "toolbar1",
                "type": "menu"
              },
              "events": {
                "buttonclick": "Wb.tip(button.text + ' clicked');\nthis.parent.close();"
              },
              "_expanded": true,
              "hasDupCid": 1,
              "items": [
                {
                  "_icon": "item",
                  "text": "item1",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item1",
                    "icon": "favorite",
                    "text": "Favorite Pages"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item2",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item2",
                    "icon": "gear",
                    "text": "Setting"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item3",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item3",
                    "icon": "command",
                    "text": "Command"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item4",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item4",
                    "icon": "word",
                    "text": "Export to Word"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item5",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item5",
                    "icon": "excel",
                    "text": "Export to Excel"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  }
                },
                {
                  "_icon": "item",
                  "text": "item6",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item6",
                    "icon": "print",
                    "text": "Print"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "item7",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "item7",
                    "icon": "table",
                    "text": "Show Table"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Window and Tip",
            "cls": "w-title3"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "winTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "winTitle",
            "title": "Window List"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "winCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "winCt",
            "layout": "form",
            "frame": "true",
            "cls": "w-rel",
            "height": "20em"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "dynamicNewWinBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "dynamicNewWinBtn",
                "text": "Dynamic new window"
              },
              "events": {
                "click": "let win = app.dynamicNewWin;\n\ndelete win.cid; //Prevent conflicts with the instance cid\nwin.owner = parentContainer; //make the win automatically destroy when parentContainer is destroyed\n\n//You can render the win to winCt by the following code:\n//win.renderEl = app.winCt.el; //Render to winCt, default render to BodyEl\n\nwin.x = Wb.random(70) + 'vw';\nwin.y = Wb.random(70) + 'vh';\nwin = Wb.create(win);\nwin.show();"
              }
            },
            {
              "_icon": "button",
              "text": "showModalWinBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "showModalWinBtn",
                "text": "Show modal window"
              },
              "events": {
                "click": "app.modalWin.show();"
              }
            },
            {
              "_icon": "button",
              "text": "showAutoResetWinBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "showAutoResetWinBtn",
                "text": "Show auto reset window"
              },
              "events": {
                "click": "app.autoResetWin.show();"
              }
            },
            {
              "_icon": "window",
              "text": "normalWin",
              "cls": "Wb.Window",
              "_expanded": true,
              "properties": {
                "cid": "normalWin",
                "title": "Normal window",
                "width": "20em",
                "height": "10em",
                "visible": "true",
                "icon": "edit",
                "html": "Hide on Close",
                "layout": "center"
              },
              "events": {
                "hide": "Wb.tip('Show again after 2 secs.')\nsetTimeout(f => app.normalWin.show().el.fadeIn(), 2000);"
              }
            },
            {
              "_icon": "window",
              "text": "dialogWin",
              "cls": "Wb.Window",
              "_expanded": true,
              "properties": {
                "cid": "dialogWin",
                "title": "Dialog window without modal",
                "width": "20em",
                "height": "10em",
                "visible": "true",
                "dialog": "true",
                "modal": "false",
                "closeAction": "destroy",
                "html": "Destroy on Close",
                "layout": "center",
                "x": "23em",
                "collapsible": "true"
              },
              "events": {
                "destroy": "Wb.tip(this.cid + ' is destroyed.');",
                "ok": "Wb.tip('You click \"' + Str.ok + '\" button.');\n// Close the window after performing asynchronous operation\n// let me = this;\n// Wb.ajax({\n//   url,\n//   success(resp) {\n//     me.close();\n//   }\n// });"
              }
            },
            {
              "_icon": "window",
              "text": "noTitleWin",
              "cls": "Wb.Window",
              "_expanded": true,
              "properties": {
                "cid": "noTitleWin",
                "visible": "true",
                "titleBar": "false",
                "layout": "grid1",
                "labelUp": "true",
                "x": "45em"
              },
              "items": [
                {
                  "_icon": "line",
                  "text": "line1",
                  "cls": "Wb.Line",
                  "properties": {
                    "cid": "line1",
                    "title": "No title window",
                    "dimTitle": "true"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "text",
                  "text": "text1",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "text1",
                    "text": "Your name"
                  },
                  "hasDupCid": 1
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "messageTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "messageTitle",
            "title": "Message"
          }
        },
        {
          "_icon": "container",
          "text": "messageCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "messageCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "infoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "infoBtn",
                "text": "Info"
              },
              "events": {
                "click": "Wb.info('Some information', f => Wb.tip('do something after show message'));"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "htmlInfoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "htmlInfoBtn",
                "text": "HTML Info"
              },
              "events": {
                "click": "Wb.info('<span class=\"w-error-color\">HTML information</span>', null, true);"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "succBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "succBtn",
                "text": "Succ"
              },
              "events": {
                "click": "Wb.succ('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "warnBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "warnBtn",
                "text": "Warn"
              },
              "events": {
                "click": "Wb.warn('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "errorBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "errorBtn",
                "text": "Error"
              },
              "events": {
                "click": "Wb.error('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "confirmBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "confirmBtn",
                "text": "Confirm"
              },
              "events": {
                "click": "Wb.confirm('Are you sure you want to delete?', f => Wb.tip('click OK'), f => Wb.tip('click Cancel'));"
              }
            },
            {
              "_icon": "button",
              "text": "chooseBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "chooseBtn",
                "text": "Choose"
              },
              "events": {
                "click": "Wb.choose('Are you sure you want to save?', button => Wb.tip(button));"
              }
            },
            {
              "_icon": "button",
              "text": "customizeBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "customizeBtn",
                "text": "Customize"
              },
              "events": {
                "click": "Wb.Window.showMessage({\n  title: 'Customize', text: 'Some message', icon: 'share',\n  name: 'sys.dialog', //name for shareing one dialog,\n  buttons: [\n    // The first button is the default enter button (press Enter to do), for more details see enterButton docs.\n    { text: 'Primary', type: 'primary', cid: 'primary' },\n    // The last button is the default esc button (press ESC or close dialog to do), for more details see escButton docs.\n    { text: Str.cancel, cid: 'cancel' }\n  ], callback(btn) {\n    this.close();\n    Wb.tip(btn + ' clicked');\n  }\n});"
              }
            },
            {
              "_icon": "button",
              "text": "fadeAnimateBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "fadeAnimateBtn",
                "text": "Fade Animate"
              },
              "events": {
                "click": "Wb.error('Some message').el.fadeIn();"
              }
            },
            {
              "_icon": "button",
              "text": "slideAnimateBtn",
              "cls": "Wb.Button",
              "_expanded": true,
              "properties": {
                "cid": "slideAnimateBtn",
                "text": "Slide Animate"
              },
              "events": {
                "click": "Wb.info('Some message', function () {\n  this.show().el.slideOut({ callback: f => this.hide() });\n}).el.slideIn();"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "tipTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tipTitle",
            "title": "Tip"
          }
        },
        {
          "_icon": "container",
          "text": "tipCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "tipCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "infoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "infoBtn",
                "text": "Info"
              },
              "events": {
                "click": "Wb.tip('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "htmlInfoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "htmlInfoBtn",
                "text": "HTML tip"
              },
              "events": {
                "click": "Wb.tip('<span class=\"w-error-color\">HTML information</span>', true);"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "succBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "succBtn",
                "text": "Succ"
              },
              "events": {
                "click": "Wb.tipSucc('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "warnBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "warnBtn",
                "text": "Warn"
              },
              "events": {
                "click": "Wb.tipWarn('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "errorBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "errorBtn",
                "text": "Error"
              },
              "events": {
                "click": "Wb.tipError('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "tipSelectBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "tipSelectBtn",
                "text": "Tip Select"
              },
              "events": {
                "click": "Wb.tipSelect();"
              }
            },
            {
              "_icon": "button",
              "text": "tipDoneBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "tipDoneBtn",
                "text": "Tip Done"
              },
              "events": {
                "click": "Wb.tipDone(Str.save);"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "toastTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "toastTitle",
            "title": "Toast"
          }
        },
        {
          "_icon": "container",
          "text": "toastCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "toastCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "infoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "infoBtn",
                "text": "Info"
              },
              "events": {
                "click": "Wb.toast('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "htmlInfoBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "htmlInfoBtn",
                "text": "HTML tip"
              },
              "events": {
                "click": "Wb.toast('<span class=\"w-error-color\">HTML information</span>', true);"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "succBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "succBtn",
                "text": "Succ"
              },
              "events": {
                "click": "Wb.toastSucc('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "warnBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "warnBtn",
                "text": "Warn"
              },
              "events": {
                "click": "Wb.toastWarn('Some information');"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "errorBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "errorBtn",
                "text": "Error"
              },
              "events": {
                "click": "Wb.toastError('Some information');"
              },
              "hasDupCid": 1
            }
          ]
        },
        {
          "_icon": "title",
          "text": "dialogTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dialogTitle",
            "title": "Dialog"
          }
        },
        {
          "_icon": "container",
          "text": "dialogCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "dialogCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "normalBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "normalBtn",
                "text": "Normal dialog"
              },
              "events": {
                "click": "app.normalDialog.show();"
              }
            },
            {
              "_icon": "button",
              "text": "customBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "customBtn",
                "text": "Custom dialog"
              },
              "events": {
                "click": "app.customDialog.show();"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "drawerTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "drawerTitle",
            "title": "Drawer"
          }
        },
        {
          "_icon": "container",
          "text": "drawerCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "drawerCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "defaultBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "defaultBtn",
                "text": "Default drawer"
              },
              "events": {
                "click": "app.defaultDrawer.show();"
              }
            },
            {
              "_icon": "button",
              "text": "topBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "topBtn",
                "text": "Top drawer"
              },
              "events": {
                "click": "app.topDrawer ??= new Wb.Drawer({ dockPosition: 'top', height: '40vh', title: 'Top Drawer', plainTitle: true });\napp.topDrawer.show();"
              }
            },
            {
              "_icon": "button",
              "text": "rightBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "rightBtn",
                "text": "Right drawer"
              },
              "events": {
                "click": "app.rightDrawer.show();"
              }
            },
            {
              "_icon": "button",
              "text": "leftBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "leftBtn",
                "text": "Left drawer"
              },
              "events": {
                "click": "app.leftDrawer ??= new Wb.Drawer({ dockPosition: 'left', width: '40vw', title: 'Left Drawer' });\napp.leftDrawer.show();"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "tooltipTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "tooltipTitle",
            "title": "Tooltip"
          }
        },
        {
          "_icon": "container",
          "text": "tooltipCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "tooltipCt",
            "layout": "form",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "component",
              "text": "tipComp",
              "cls": "Wb.Component",
              "properties": {
                "cid": "tipComp",
                "tip": "Common tip message",
                "text": "Move the mouse here",
                "padding": "1em",
                "frame": "true"
              }
            },
            {
              "_icon": "component",
              "text": "retainTipComp",
              "cls": "Wb.Component",
              "properties": {
                "cid": "retainTipComp",
                "tip": "{enterRetain: true, html: 'When the pointer is in the Tip, it will not auto hide.'}",
                "text": "Retain tip",
                "padding": "1em",
                "frame": "true"
              }
            },
            {
              "_icon": "component",
              "text": "customTip",
              "cls": "Wb.Component",
              "properties": {
                "cid": "customTip",
                "text": "Custom retain tip",
                "padding": "1em",
                "frame": "true"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "info",
                  "text": "tip",
                  "cls": "Wb.Tip",
                  "properties": {
                    "cid": "tip",
                    "isProperty": "true",
                    "layout": "grid1",
                    "enterRetain": "true",
                    "width": "40em",
                    "gap": "1em",
                    "frame": "true"
                  },
                  "_expanded": true,
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "label",
                      "text": "label1",
                      "cls": "Wb.Label",
                      "properties": {
                        "cid": "label1",
                        "titleType": "title4",
                        "text": "Some descriptions"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "text",
                      "text": "text1",
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "text1",
                        "text": "Text"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "number-edit",
                      "text": "number1",
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "number1",
                        "text": "Number"
                      },
                      "hasDupCid": 0
                    },
                    {
                      "_icon": "label1",
                      "text": "displayField1",
                      "cls": "Wb.DisplayField",
                      "properties": {
                        "cid": "displayField1",
                        "text": "Display Field",
                        "value": "Some value"
                      },
                      "hasDupCid": 0
                    },
                    {
                      "_icon": "button",
                      "text": "button1",
                      "cls": "Wb.Button",
                      "properties": {
                        "cid": "button1",
                        "text": "Button",
                        "icon": "button"
                      },
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "component",
              "text": "customShareTip1",
              "cls": "Wb.Component",
              "properties": {
                "cid": "customShareTip1",
                "text": "Custom share tip 1",
                "padding": "1em",
                "tip": "@app.myShareTip",
                "frame": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "component",
              "text": "customShareTip2",
              "cls": "Wb.Component",
              "properties": {
                "cid": "customShareTip2",
                "text": "Custom share tip 2",
                "padding": "1em",
                "tip": "@app.myShareTip",
                "frame": "true"
              },
              "_expanded": true
            },
            {
              "_icon": "component",
              "text": "clickToTipComp",
              "cls": "Wb.Component",
              "properties": {
                "cid": "clickToTipComp",
                "text": "Click to tip",
                "padding": "1em",
                "frame": "true"
              },
              "events": {
                "click": "Wb.tipAt('Some message', e.x, e.y);"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "true",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Select and download data\n   */\n  select() {\n    let download = Wb.getObject('download');\n    if (download) {\n      //download blob\n      let row = Wb.getRow({\n        sql: 'select full_name, photo from wb_staff where sid={?$sid?}',\n        blob: true,\n        params: download\n      });\n      if (row?.photo)\n        Wb.exportData(row.photo, row.full_name + '.png');\n      else\n        Wb.raise('Photo not found.');\n    } else {\n      let sql;\n\n      sql = `\n        select a.*,b.user_name,c.dept_name,c.dept_code from wb_staff a, wb_user b, wb_dept c\n        where a.user_id=b.sid and a.dept_id=c.sid\n        `;\n      if (Params.search) {\n        Wb.setLike('search');\n        sql += ' and (a.full_name like {?search?} or a.code like {?search?} or b.user_name like {?search?})';\n      }\n      // getOrderSql will not cause SQL injection\n      sql += Wb.getOrderSql({ email: 'a.email' });// There are 2 emails fields in the tables\n      Wb.sendRowx({ sql, kv: { gender: 'gender' } });\n    }\n  },\n  /**\n   * Select and download data by using dict.\n   */\n  dictSelect() {\n    if (Wb.has('download')) {\n      this.select(); // invoke select download handler\n    } else {\n      let sql;\n      sql = `\n        select a.sid, a.code, a.full_name, a.user_id, b.user_name as user_name_bind,\n        a.dept_id, c.dept_code as dept_code_hide, c.dept_name as dept_name_tree, a.birth_date, a.gender, a.height, \n        a.email, a.salary, a.cv, a.photo from wb_staff a, wb_user b, wb_dept c\n        where a.user_id=b.sid and a.dept_id=c.sid\n        `;\n      if (Params.search) {\n        Wb.setLike('search');\n        sql += ' and (a.full_name like {?search?} or a.code like {?search?} or b.user_name like {?search?})';\n      }\n      // getOrderSql will not cause SQL injection\n      sql += Wb.getOrderSql({ email: 'a.email', user_name_bind: 'b.user_name', dept_name_tree: 'c.dept_name' });\n      Wb.sendDict(sql, 'wb,');\n    }\n  },\n  /**\n   * select usernames.\n   */\n  selectUsers() {\n    let sql;\n\n    sql = 'select sid,user_name from wb_user';\n    if (Params.query) {\n      Wb.setLike('query');\n      sql += ' where user_name like {?query?}';\n    }\n    sql += ' order by user_name';\n    Wb.sendRows(sql);\n  },\n  /**\n   * select depts.\n   */\n  selectDepts() {\n    let sql;\n\n    sql = 'select sid,dept_name from wb_dept';\n    if (Params.query) {\n      Wb.setLike('query');\n      sql += ' where dept_name like {?query?}';\n    }\n    sql += ' order by dept_name';\n    Wb.sendRows(sql);\n  },\n  /**\n   * Save data.\n   */\n  save() {\n    let sid, rec, insert = [];\n\n    Params.insert?.forEach(item => {\n      sid = Wb.getId();\n      rec = { sid };\n      Wb.apply(item, rec);\n      insert.push(rec);\n    });\n    Wb.sync({\n      tableName: 'wb_staff',\n      insert: Params.insert,\n      update: Params.update,\n      del: Params.del\n    });\n    // Another way for executing sync:\n    // Wb.set({tableName: 'wb_staff'}); //Warning: Must set tableName otherwise tableName in parameters will be used\n    // Wb.sync(Params);\n    Wb.send({ insert });\n  },\n  /**\n   * Add record.\n   */\n  add() {\n    let rec = { sid: Wb.getId() };\n\n    Wb.set(rec);\n    Wb.sync({ tableName: 'wb_staff', insert: Params });\n    Wb.send(rec);\n  },\n  /**\n   * Update record.\n   */\n  edit() {\n    Wb.sync({ tableName: 'wb_staff', update: Params });\n  },\n  /**\n   * Delete records.\n   */\n  del() {\n    let del = Wb.getObject('del');\n\n    Wb.sync({ tableName: 'wb_staff', del });\n  },\n  /**\n   * Get photo.\n   */\n  getPhoto() {\n    let row = Wb.getRow({\n      sql: 'select photo from wb_staff where sid={?sid?}',\n      blob: true\n    });\n    if (row?.photo)\n      Wb.exportData(row.photo);\n    else\n      Wb.exportData(new Wb.File(true, 'wb/images/null.png').stream);\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: dict-edit-dialog.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Object} - The edit window configs object. */\n  winConfigs: {\n    events: {\n      ready() {\n        let win = this, ct = new Wb.Container({ layout: 'grid2', autoGrid: 'up' }), cv, photo;\n\n        //add all items to ct without cv\n        ct.add(win.items.filter(item => item.cid != 'cv'));\n\n        //add titles and ct to win\n        win.layout = 'form1';\n        win.insert(0, { cname: 'title', title: 'General Information' });\n        win.insert(1, ct);\n        win.insert(2, { cname: 'title', title: 'Personal Resume' });\n\n        //customize cv\n        cv = win.down('cv');\n        Wb.apply(cv, { flex: 1, text: null, minHeight: '5em' });\n\n        //customize photo\n        photo = ct.down('photo');\n        //Insert to 2nd position\n        ct.insert(1, photo);\n        //You can configure these properties into the dictionary\n        Wb.apply(photo, {\n          gridRow: 'span 3', browseMode: true, browseHeight: '8em', browseWidth: '12em', fileButtons: false\n        });\n\n        //insert table style toggle button\n        win.buttonsBar.insert(0, [{\n          cname: 'button', active: false, text: 'Table Style', plainIcon: true, icon: 'table', handler() {\n            ct.tableStyle = !ct.tableStyle;\n          }\n        }, '->']);\n      }\n    }\n  },\n  /**\n   * Method to execute when adding or updating data fails.\n   * @param {String} resp Response text.\n   */\n  onFailure(resp) {\n    Wb.checkExists(resp, 'wb_staff_code', app.grid1.dictWin.down('code'));\n  }\n});"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.grid1.dictAdd({\n  url: xpath + '/../actions&xaction=add',\n  failure: app.onFailure\n}, null, null, null, app.winConfigs);"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "let win = app.grid1.dictEdit({\n  url: xpath + '/../actions&xaction=edit',\n  failure: app.onFailure\n}, 'code', null, null, app.winConfigs);\nlet rec = app.grid1.selection;\nif (rec)\n  win.down('photo').preview(xpath + '/../actions&xaction=getPhoto&sid=' + rec.data.sid + '&' + Wb.getId());"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.removeRecords(xpath + '/../actions&xaction=del', 'code');"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/../actions&xaction=dictSelect'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "code",
            "stateId": "wb.crud-dialog"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');",
            "beforeloadcolumns": "// Customize columns from the dict regardless of whether this grid is in a saved state.\nlet col = columns.find(col => col.fieldName == 'full_name');\n\nif (col)\n  col.width = '12em';\n\n// remove default row number column\n// columns.removeBy(col => col.rowNum);"
          }
        }
      ]
    }
  ]
}

File name: dict-edit-grid.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Fired after the table data is changed. @priv\n   */\n  onChange() {\n    app.saveBtn.disabled = false;\n  },\n  /**\n   * Save grid data.\n   * @param {Function} [callback] Callback function after success.\n   */\n  save(callback) {\n    app.grid1.sync({\n      url: xpath + '/../actions&xaction=save',\n      success() {\n        app.saveBtn.disabled = true;\n        callback?.();\n        //Wb.tip('Successfully saved');\n      },\n      failure(resp) {\n        Wb.checkExists(resp, 'wb_staff_code', Str.code);\n      }\n    });\n  }\n});",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.grid1.addRecord();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.delRecords();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "saveBtn",
                "icon": "save",
                "text": "@Str.save",
                "keys": "Ctrl+S",
                "disabled": "true"
              },
              "events": {
                "click": "app.save();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/../actions&xaction=dictSelect'",
            "editable": "true",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "code"
          },
          "_expanded": true,
          "events": {
            "editing": "app.onChange();",
            "itemchange": "app.onChange();",
            "beforeedit": "//Set photo file name\nif (column.fieldName == 'photo') {\n  let value = column.render.call(row, configs.value, row.data);\n  if (value)\n    configs.value = value;\n}",
            "beforeload": "return this.saveConfirm(app.saveBtn, app.save);"
          }
        }
      ]
    }
  ]
}

File name: edit-dialog.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "editWin",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "editWin",
        "layout": "grid2",
        "resetDialog": "true",
        "autoGrid": "true"
      },
      "items": [
        {
          "_icon": "title",
          "text": "generalTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "generalTitle",
            "title": "General Information"
          }
        },
        {
          "cls": "Wb.Text",
          "properties": {
            "cid": "code",
            "required": "true",
            "maxLength": "5",
            "text": "Code"
          },
          "text": "code",
          "_expanded": true,
          "_icon": "text"
        },
        {
          "_icon": "search-file",
          "text": "photo",
          "cls": "Wb.FileInput",
          "properties": {
            "cid": "photo",
            "accept": ".png",
            "text": "Photo",
            "gridRow": "span 3",
            "browseMode": "true",
            "browseHeight": "8em"
          }
        },
        {
          "cls": "Wb.Text",
          "properties": {
            "cid": "full_name",
            "required": "true",
            "text": "Full name"
          },
          "text": "full_name",
          "_expanded": true,
          "_icon": "text"
        },
        {
          "_icon": "combo",
          "text": "user_name",
          "cls": "Wb.Select",
          "properties": {
            "cid": "user_name",
            "textField": "user_name",
            "valueField": "sid",
            "url": "@xpath + '/../actions&xaction=selectUsers'",
            "required": "true",
            "forceSelect": "true",
            "text": "Username",
            "bindField": "user_id"
          }
        },
        {
          "_icon": "combo",
          "text": "dept_name",
          "cls": "Wb.Select",
          "properties": {
            "cid": "dept_name",
            "url": "m?xwl=sys/dialog/dept-selector/select",
            "textField": "dept_name",
            "clearButton": "true",
            "treePicker": "true",
            "subtextField": "dept_code",
            "required": "true",
            "text": "Dept name",
            "gridColumn": "span 2",
            "valueMap": "{ sid: 'dept_id', dept_name: 'dept_name', dept_code: 'dept_code' }"
          }
        },
        {
          "_icon": "calendar",
          "text": "birth_date",
          "cls": "Wb.Date",
          "properties": {
            "cid": "birth_date",
            "text": "Birth date"
          }
        },
        {
          "_icon": "combo",
          "text": "gender",
          "cls": "Wb.Select",
          "properties": {
            "cid": "gender",
            "text": "Gender",
            "keyName": "gender"
          }
        },
        {
          "_icon": "number-edit",
          "text": "height",
          "cls": "Wb.Number",
          "properties": {
            "cid": "height",
            "minValue": "0",
            "maxValue": "999",
            "decimalCount": "0",
            "text": "Height"
          }
        },
        {
          "cls": "Wb.Text",
          "properties": {
            "cid": "email",
            "valueType": "email",
            "text": "Email"
          },
          "text": "email",
          "_expanded": true,
          "_icon": "text"
        },
        {
          "_icon": "number-edit",
          "text": "salary",
          "cls": "Wb.Number",
          "properties": {
            "cid": "salary",
            "decimalCount": "2",
            "text": "Salary"
          }
        },
        {
          "_icon": "title",
          "text": "cvTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "cvTitle",
            "title": "Personal Resume"
          }
        },
        {
          "_icon": "textarea",
          "text": "cv",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "cv",
            "gridColumn": "span 2",
            "height": "8em"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit",
        "autoContextMenu": "true"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "// Preferred way:\n// app.grid1.insertRecord({\n//   url: xpath + '/../actions&xaction=add',\n//   failure(resp) {\n//     Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n//   }\n// }, app.editWin);\n\n\n// The following code demonstrates the detailed implementation\nlet win = app.editWin;\n\nwin.icon = 'add';\nwin.title = Str.add;\nwin.focusControl = null;\nwin.okHandler = (win, values) => {\n  Wb.ajax({\n    url: xpath + '/../actions&xaction=add',\n    params: values,\n    json: true,\n    success(resp) {\n      app.grid1.appendRecord(Wb.apply(values, resp));\n      win.hide();\n    },\n    failure(resp) {\n      Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n    }\n  });\n};\nwin.show();\nwin.scrollEl.scrollTop = win.scrollEl.scrollLeft = 0;"
              }
            },
            {
              "_icon": "item",
              "text": "editBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "editBtn",
                "text": "@Str.edit",
                "icon": "edit",
                "keys": "Ctrl+J"
              },
              "events": {
                "click": "// Preferred way:\n// let rec = app.grid1.selection;\n// app.grid1.editRecord({\n//   url: xpath + '/../actions&xaction=edit',\n//   failure(resp) {\n//     Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n//   }\n// }, app.editWin);\n// if (rec)\n//   app.editWin.down('photo').preview(xpath + '/../actions&xaction=getPhoto&sid=' + data.sid + '&' + Wb.getId());\n\n\n// The following code demonstrates the detailed implementation\nlet win = app.editWin, rec = app.grid1.selection, data;\n\nif (!rec) {\n  Wb.tipSelect();\n  return;\n}\ndata = rec.data;\nwin.icon = 'edit';\nwin.title = Str.edit + ' - ' + data.code;\nwin.focusControl = true;\nWb.setValue(win, data);\nwin.down('photo').preview(xpath + '/../actions&xaction=getPhoto&sid=' + data.sid + '&' + Wb.getId());\nwin.okHandler = (win, values) => {\n  Wb.ajax({\n    url: xpath + '/../actions&xaction=edit',\n    params: rec.getDiffer(values),\n    json: true,\n    success(resp) {\n      rec.update(Wb.apply(values, resp));\n      win.hide();\n    },\n    failure(resp) {\n      Wb.checkExists(resp, 'wb_staff_code', app.editWin.down('code'));\n    }\n  });\n};\nwin.show();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "// Preferred way:\n// app.grid1.removeRecords( xpath + '/../actions&xaction=del', 'code');\n\n\n// The following code demonstrates the detailed implementation\nlet grid = app.grid1, recs = grid.selections;\nif (!recs.length) {\n  Wb.tipSelect();\n  return;\n}\nWb.confirm(Wb.getActionHint(recs, 'code'), f => {\n  Wb.ajax({\n    url: xpath + '/../actions&xaction=del',\n    params: { del: grid.originData },\n    success() {\n      grid.delRecords();\n    }\n  });\n});"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/../actions&xaction=select'",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "code",
            "stateId": "wb.crud-dialog"
          },
          "_expanded": true,
          "events": {
            "itemdblclick": "app.editBtn.fireEvent('click');"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "codeCol",
                    "fieldName": "code",
                    "text": "Code",
                    "width": "5em"
                  },
                  "text": "codeCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fullNameCol",
                    "fieldName": "full_name",
                    "text": "Full name",
                    "width": "13em"
                  },
                  "text": "fullNameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "usernameCol",
                    "fieldName": "user_name",
                    "text": "Username"
                  },
                  "text": "usernameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "deptCol",
                    "fieldName": "dept_name",
                    "text": "Department",
                    "render": "Wb.Column.addSubText(el, value, data.dept_code);"
                  },
                  "text": "deptCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "birthDateCol",
                    "fieldName": "birth_date",
                    "text": "Birth date"
                  },
                  "text": "birthDateCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "genderCol",
                    "fieldName": "gender",
                    "text": "Gender",
                    "width": "6em",
                    "keyValue": "true"
                  },
                  "text": "genderCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "heightCol",
                    "fieldName": "height",
                    "text": "Height",
                    "width": "6em"
                  },
                  "text": "heightCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "emailCol",
                    "fieldName": "email",
                    "text": "Email",
                    "width": "18em"
                  },
                  "text": "emailCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "salaryCol",
                    "fieldName": "salary",
                    "text": "Salary",
                    "type": "usd",
                    "width": "9em"
                  },
                  "text": "salaryCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "cvCol",
                    "fieldName": "cv",
                    "text": "CV",
                    "width": "15em",
                    "sortable": "false"
                  },
                  "text": "cvCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "photoCol",
                    "fieldName": "photo",
                    "text": "Photo",
                    "width": "15em",
                    "render": "if (value)\n  Wb.Column.createDownload(el, column.fieldName, data.full_name + '.png');",
                    "sortable": "false"
                  },
                  "text": "photoCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: edit-grid.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Fired after the table data is changed. @priv\n   */\n  onChange() {\n    app.saveBtn.disabled = false;\n  },\n  /**\n   * Save grid data.\n   * @param {Function} [callback] Callback function after success.\n   */\n  save(callback) {\n    app.grid1.sync({\n      url: xpath + '/../actions&xaction=save',\n      success() {\n        app.saveBtn.disabled = true;\n        callback?.();\n        //Wb.tip('Successfully saved');\n      },\n      failure(resp) {\n        // Preferred way:\n        // Wb.checkExists(resp, 'wb_staff_code', Str.code);\n        if (resp?.toLowerCase().includes('wb_staff_code'))\n          Wb.error(Str.duplicate.format(Str.code));\n      }\n    });\n  }\n});",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "addBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "addBtn",
                "text": "@Str.add",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.grid1.addRecord();"
              }
            },
            {
              "_icon": "item",
              "text": "delBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "delBtn",
                "text": "@Str.del",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "app.grid1.delRecords();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "saveBtn",
                "icon": "save",
                "text": "@Str.save",
                "keys": "Ctrl+S",
                "disabled": "true"
              },
              "events": {
                "click": "app.save();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.grid1.delayLoad({ comps: app.search });"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/../actions&xaction=select'",
            "editable": "true",
            "multiSelect": "true",
            "columnsSortable": "true",
            "sorters": "code"
          },
          "_expanded": true,
          "events": {
            "editing": "app.onChange();",
            "itemchange": "app.onChange();",
            "beforeedit": "//Set photo file name\nif (column.fieldName == 'photo') {\n  let value = column.render.call(row, configs.value, row.data);\n  if (value)\n    configs.value = value;\n}",
            "beforeload": "// Preferred way:\n// return this.saveConfirm(app.saveBtn, app.save);\nlet grid = this, saveBtn = app.saveBtn;\n//saveBtn is null before initialized\nif (saveBtn && !saveBtn.disabled) {\n  Wb.choose(Str.modifiedConfirm.format(Str.list), btn => {\n    if (btn == 'yes')\n      app.save(f => grid.reload());\n    else if (btn == 'no') {\n      saveBtn.disabled = true;\n      grid.reload();\n    }\n  });\n  return false;\n}"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "codeCol",
                    "fieldName": "code",
                    "text": "Code",
                    "width": "5em"
                  },
                  "text": "codeCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true",
                        "maxLength": "5"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "fullNameCol",
                    "fieldName": "full_name",
                    "text": "Full name",
                    "width": "13em"
                  },
                  "text": "fullNameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "usernameCol",
                    "fieldName": "user_name",
                    "text": "Username"
                  },
                  "text": "usernameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "combo",
                      "text": "editor",
                      "cls": "Wb.Select",
                      "properties": {
                        "cid": "editor",
                        "textField": "user_name",
                        "valueField": "sid",
                        "isProperty": "true",
                        "url": "@xpath + '/../actions&xaction=selectUsers'",
                        "required": "true",
                        "forceSelect": "true",
                        "bindField": "user_id"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "deptCol",
                    "fieldName": "dept_name",
                    "text": "Department",
                    "width": "20em",
                    "render": "Wb.Column.addSubText(el, value, data.dept_code);"
                  },
                  "text": "deptCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "combo",
                      "text": "editor",
                      "cls": "Wb.Select",
                      "properties": {
                        "cid": "editor",
                        "url": "m?xwl=sys/dialog/dept-selector/select",
                        "textField": "dept_name",
                        "treePicker": "true",
                        "subtextField": "dept_code",
                        "valueMap": "{ sid: 'dept_id', dept_name: 'dept_name', dept_code: 'dept_code' }",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "birthDateCol",
                    "fieldName": "birth_date",
                    "text": "Birth date"
                  },
                  "text": "birthDateCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "calendar",
                      "text": "editor",
                      "cls": "Wb.Date",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "genderCol",
                    "fieldName": "gender",
                    "text": "Gender",
                    "width": "6em",
                    "keyValue": "true"
                  },
                  "text": "genderCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "combo",
                      "text": "editor",
                      "cls": "Wb.Select",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "keyName": "gender"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "heightCol",
                    "fieldName": "height",
                    "text": "Height",
                    "width": "6em"
                  },
                  "text": "heightCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "number-edit",
                      "text": "editor",
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "minValue": "0",
                        "maxValue": "999",
                        "decimalCount": "0"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "emailCol",
                    "fieldName": "email",
                    "text": "Email",
                    "width": "18em"
                  },
                  "text": "emailCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "valueType": "email"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "salaryCol",
                    "fieldName": "salary",
                    "text": "Salary",
                    "type": "usd",
                    "width": "9em"
                  },
                  "text": "salaryCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "number-edit",
                      "text": "editor",
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "decimalCount": "2"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "cvCol",
                    "fieldName": "cv",
                    "text": "CV",
                    "width": "15em",
                    "sortable": "false"
                  },
                  "text": "cvCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "textarea",
                      "text": "editor",
                      "cls": "Wb.TextArea",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "maximizable": "true"
                      },
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "photoCol",
                    "fieldName": "photo",
                    "text": "Photo",
                    "width": "15em",
                    "render": "if (value)\n  return value instanceof File ? value.name : '(photo)';",
                    "sortable": "false"
                  },
                  "text": "photoCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "_icon": "search-file",
                      "text": "editor",
                      "cls": "Wb.FileInput",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "accept": ".png",
                        "download": "true"
                      },
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: select-staff.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "true",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.sendRowx('select * from wb_staff');"
  },
  "_icon": "module"
}

File name: staff.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "url": "@xpath + '/../select-staff'"
          }
        }
      ]
    }
  ]
}

File name: card.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Init the module.\n   */\n  init() {\n    // app.date1.focus();\n  }\n});"
  },
  "items": [
    {
      "_icon": "panel",
      "text": "panel1",
      "cls": "Wb.Panel",
      "_expanded": true,
      "properties": {
        "cid": "panel1",
        "layout": "grid1"
      },
      "items": [
        {
          "_icon": "label",
          "text": "label1",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "label1",
            "text": "This card from card.xwl"
          }
        },
        {
          "_icon": "text",
          "text": "text1",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "text1",
            "text": "Text"
          }
        },
        {
          "_icon": "calendar",
          "text": "date1",
          "cls": "Wb.Date",
          "properties": {
            "cid": "date1",
            "text": "Date"
          }
        },
        {
          "_icon": "button",
          "text": "setValueBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "setValueBtn",
            "text": "Set text value",
            "width": "20em",
            "justifySelf": "center"
          },
          "events": {
            "click": "app.text1.value = 'card value';"
          }
        },
        {
          "_icon": "button",
          "text": "setParentTitleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "setParentTitleBtn",
            "text": "Set parent module tab title",
            "width": "20em",
            "justifySelf": "center"
          },
          "events": {
            "click": "parentApp.runModuleCardTab.title = 'New Title';"
          }
        }
      ]
    },
    {
      "_icon": "window",
      "text": "thisWinAutoDestroyedAfterOwnerDestroyed",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "thisWinAutoDestroyedAfterOwnerDestroyed"
      }
    }
  ]
}

File name: common-window.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Function} callback The callback function. @priv */\n  /**\n   * Perform add.\n   * @param {Number} value1 value 1.\n   * @param {Number} value2 value 2.\n   * @param {Function} callback The callback function after completion.\n   * @param {Number} .total The total value.\n   * @param {Wb.Window} .win The edit window.\n   */\n  add(value1, value2, callback) {\n    app.number1.value = value1;\n    app.number2.value = value2;\n    app.callback = callback;\n    app.window1.show();\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "Perform add",
        "layout": "grid1",
        "visible": "true",
        "resetDialog": "true",
        "closeAction": "destroy"
      },
      "events": {
        "ok": "app.callback(app.number1.value + app.number2.value, this);"
      },
      "items": [
        {
          "_icon": "label",
          "text": "label1",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "label1",
            "text": "This window from common-window.xwl"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number1",
          "cls": "Wb.Number",
          "properties": {
            "cid": "number1",
            "required": "true",
            "text": "Value1"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number2",
          "cls": "Wb.Number",
          "_expanded": true,
          "properties": {
            "cid": "number2",
            "required": "true",
            "text": "Value2"
          }
        }
      ]
    },
    {
      "_icon": "window",
      "text": "thisWinAutoDestroyedAfterWindow1Destroyed",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "thisWinAutoDestroyedAfterWindow1Destroyed"
      }
    }
  ]
}

File name: panel.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "panel",
      "text": "main",
      "cls": "Wb.Panel",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "grid1",
        "title": "This panel from panel.xwl",
        "closable": "true"
      },
      "items": [
        {
          "_icon": "label",
          "text": "descLabel",
          "cls": "Wb.Label",
          "_expanded": false,
          "properties": {
            "cid": "descLabel",
            "text": "The component which cid is \"main\" represents the owner of the entire module, and when the main component is destroyed, the entire module will be destroyed."
          }
        },
        {
          "_icon": "line",
          "text": "line1",
          "cls": "Wb.Line",
          "properties": {
            "cid": "line1",
            "title": "Form"
          }
        },
        {
          "_icon": "text",
          "text": "text1",
          "cls": "Wb.Text",
          "properties": {
            "cid": "text1",
            "text": "Text"
          },
          "_expanded": true
        },
        {
          "_icon": "number-edit",
          "text": "number1",
          "cls": "Wb.Number",
          "properties": {
            "cid": "number1",
            "text": "Number"
          }
        }
      ]
    },
    {
      "_icon": "window",
      "text": "thisWinAutoDestroyedAfterMainDestroyed",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "thisWinAutoDestroyedAfterMainDestroyed"
      }
    }
  ]
}

File name: single-window.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Execute every time when this module is loaded, regardless of whether singleton or not. @priv\n   */\n  onLoad() {\n    app.number3.value = Wb.random(100);\n  },\n  /** @property {Function} callback The callback function. @priv */\n  /**\n   * Perform add.\n   * @param {Number} value1 value 1.\n   * @param {Number} value2 value 2.\n   * @param {Function} callback The callback function after completion.\n   * @param {Number} .total The total value.\n   * @param {Wb.Window} .win The edit window.\n   */\n  add(value1, value2, callback) {\n    app.number1.value = value1;\n    app.number2.value = value2;\n    app.callback = callback;\n    app.window1.show();\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "Perform add",
        "layout": "grid1",
        "visible": "true",
        "resetDialog": "true"
      },
      "events": {
        "ok": "app.callback(app.number1.value + app.number2.value + app.number3.value, this);"
      },
      "items": [
        {
          "_icon": "label",
          "text": "label1",
          "cls": "Wb.Label",
          "_expanded": true,
          "properties": {
            "cid": "label1",
            "text": "This window from single-window.xwl"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number1",
          "cls": "Wb.Number",
          "properties": {
            "cid": "number1",
            "required": "true",
            "text": "Value1"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number2",
          "cls": "Wb.Number",
          "_expanded": true,
          "properties": {
            "cid": "number2",
            "required": "true",
            "text": "Value2"
          }
        },
        {
          "_icon": "number-edit",
          "text": "number3",
          "cls": "Wb.Number",
          "_expanded": false,
          "properties": {
            "cid": "number3",
            "required": "true",
            "text": "Value3"
          }
        }
      ]
    }
  ]
}

File name: client-load-module.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Client load module, js, mjs and css",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "openModuleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "openModuleTitle",
            "title": "Open module"
          }
        },
        {
          "_icon": "label",
          "text": "openModuleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "openModuleLabel",
            "text": "Open module in \"main tab\" or new window."
          }
        },
        {
          "_icon": "button",
          "text": "openModuleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "openModuleBtn",
            "text": "Open module",
            "type": "primary"
          },
          "events": {
            "click": "//equals to:  Wb.run({ url: 'm?xwl=admin/role', container: Globals.WbMainTab, title: 'custom title', icon: 'gear' });\nWb.open('m?xwl=admin/role');"
          }
        },
        {
          "_icon": "title",
          "text": "openNormalModuleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "openNormalModuleTitle",
            "title": "Open module as \"normal type\""
          }
        },
        {
          "_icon": "label",
          "text": "openNormalModuleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "openNormalModuleLabel",
            "text": "Open module in \"main tab\" or new window, and allow the page to reload/refresh."
          }
        },
        {
          "_icon": "button",
          "text": "openNormalModuleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "openNormalModuleBtn",
            "text": "Open module as \"normal type\"",
            "type": "primary"
          },
          "events": {
            "click": "//equals to:  Wb.run({ url: 'm?xwl=admin/perm', container: Globals.WbMainTab, allowRefresh: true });\nWb.openNormal('m?xwl=admin/perm');"
          }
        },
        {
          "_icon": "title",
          "text": "runModulePanelTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "runModulePanelTitle",
            "title": "Run module and get panel"
          }
        },
        {
          "_icon": "label",
          "text": "runModulePanelLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "runModulePanelLabel",
            "text": "Run module and get it's content to the container."
          }
        },
        {
          "_icon": "button",
          "text": "runModulePanelBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "runModulePanelBtn",
            "text": "Run module",
            "type": "primary"
          },
          "events": {
            "click": "let ct = app.runModulePanelCt;\nct.destroyAll();\nWb.run({\n  url: xpath + '/panel',\n  success(scope) {\n    ct.add(scope.main);\n  }\n});"
          }
        },
        {
          "_icon": "container",
          "text": "runModulePanelCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "runModulePanelCt",
            "frame": "true",
            "minHeight": "10em"
          }
        },
        {
          "_icon": "title",
          "text": "runModuleCardTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "runModuleCardTitle",
            "title": "Run module and get tab card"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "runModuleCardLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "runModuleCardLabel",
            "text": "Run module and get it's content to the new tab card."
          }
        },
        {
          "_icon": "button",
          "text": "runModuleCardBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "runModuleCardBtn",
            "text": "Run module",
            "type": "primary"
          },
          "events": {
            "click": "let card;\n\napp.cardIndex ??= 1;\ncard = app.runModuleCardTab.add({ title: 'Card ' + app.cardIndex++, icon: 'card', layout: 'fit' }).show();\nWb.run({\n  url: xpath + '/card',\n  app, // pass app as parentApp, the sub module can be referred to the parent module\n  owner: card, // pass card as owner of module card.xwl, when the card is destroyed, the module will be automatically destroyed\n  cached: true, // store the module to the cache, enables the next request to fetch directly from the cache\n  success(scope) {\n    card.add(scope.panel1);\n    scope.init(); // call init function defined in card.xwl\n    //app.cardModule = scope; // save scope to app for further access, eg: app.cardModule.text1 = 'abc';\n  }\n});"
          }
        },
        {
          "_icon": "tab",
          "text": "runModuleCardTab",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "runModuleCardTab",
            "minHeight": "10em",
            "frame": "true"
          }
        },
        {
          "_icon": "title",
          "text": "invokeWindowTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "invokeWindowTitle",
            "title": "Invoke window"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "invokeWindowLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "invokeWindowLabel",
            "text": "Invoke a common window and pass callback function as parameter."
          }
        },
        {
          "_icon": "button",
          "text": "invokeWindowBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "invokeWindowBtn",
            "text": "Invoke window",
            "type": "primary"
          },
          "events": {
            "click": "Wb.run({\n  url: xpath + '/common-window',\n  owner: 'window1', //destroy the module when window1 is destroyed\n  success(scope) {\n    scope.add(3, 5, (total, win) => {\n      Wb.tip('Total value is: ' + total);\n      win.close();\n    });\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "invokeSingleModuleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "invokeSingleModuleTitle",
            "title": "Invoke single module"
          },
          "_expanded": true
        },
        {
          "_icon": "label",
          "text": "invokeSingleModuleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "invokeSingleModuleLabel",
            "text": "Invoke a single module and pass callback function as parameter."
          }
        },
        {
          "_icon": "button",
          "text": "invokeSingleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "invokeSingleBtn",
            "text": "Invoke single module",
            "type": "primary"
          },
          "events": {
            "click": "Wb.run({\n  url: xpath + '/single-window',\n  single: true, //single instance module\n  success(scope) {\n    scope.add(3, 5, (total, win) => {\n      Wb.tip('Total value is: ' + total);\n      win.close();\n    });\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "dynamicLoadJsCssTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "dynamicLoadJsCssTitle",
            "title": "Dynamic load js/css"
          }
        },
        {
          "_icon": "label",
          "text": "dynamicLoadJsCssLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "dynamicLoadJsCssLabel",
            "text": "Use [Wb.load] method to dynamic load js/css resources. Set the [links] property of the module node to static load js/mjs/css resources, you can drag js/mjs/css files to the [links] property directly."
          }
        },
        {
          "_icon": "button",
          "text": "dynamicLoadJsCssBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "dynamicLoadJsCssBtn",
            "text": "Dynamic load JS/CSS",
            "type": "primary"
          },
          "events": {
            "click": "Wb.load(\n  ['wb/css/demo.css', { url: 'wb/libs/echarts.js', async: true }],\n  f => Wb.tipSucc('The js/css resources has been successfully loaded.')\n);\n// Promise style\n// Wb.loadx(['wb/css/demo.css', { url: 'wb/libs/echarts.js', async: true }]).then(\n//   f => Wb.tipSucc('The js/css resources has been successfully loaded.'));"
          }
        },
        {
          "_icon": "label",
          "text": "importMJSLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "importMJSLabel",
            "text": "Dynamic import Javascript module."
          }
        },
        {
          "_icon": "button",
          "text": "importMJSBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "importMJSBtn",
            "text": "Dynamic import mjs",
            "type": "primary"
          },
          "events": {
            "click": "// Resources in the module directory are protected except for path containing the \"$\" character\nWb.loadModule(xpath + '/demo$', module => Wb.tip('The result is: ' + module.Demo.add(3, 4)));"
          }
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Process get request.\n   */\n  get() {\n    Wb.log(Wb.get('string'));\n    Wb.log(Params.string);\n    Wb.log(Wb.getInt('int')); //Params.int is string type\n    Wb.log(Wb.getFloat('float'));\n    Wb.log(Wb.getDate('date'));\n    Wb.log(Wb.getBool('bool'));\n    Wb.log(Wb.getObject('object'));\n    Wb.log(Wb.getObject('array'));\n    Wb.send('done');\n  },\n  /**\n   * Process post request.\n   */\n  post() {\n    let val = Wb.get('foo');\n    //For the processing of uploading files, please refer to [file-updownload] example\n    Wb.send(val);\n  },\n  /**\n   * Process object.\n   */\n  deserializeObject() {\n    //The system will automatically deserialize the values sent by the client\n    Wb.log(Wb.get('string')); // same as Params.string\n    Wb.log(Wb.get('int')); // Params.int is Number type\n    Wb.log(Params.float); // Params.float is Number type\n    Wb.log(Params.date); // Params.date is Date type\n    Wb.log(Params.bool); // Params.bool is Boolean type\n    Wb.log(Params.object);// Params.object is Object type\n    Wb.log(Params.array);// Params.array is Array type\n  },\n  /**\n   * Process multiple same parameters.\n   */\n  postMultipleParams() {\n    let arrayList = Wb.getParams('myParam');\n    Wb.log(Wb.encode(arrayList)); //Use Wb.getParams to always return array like params\n  },\n  /**\n   * Process payload data.\n   */\n  postPayload() {\n    Wb.log(Wb.payload);\n  },\n  /**\n   * Process payload json.\n   */\n  postPayloadJson() {\n    Wb.log(Wb.get('foo') + ',' + Params.bar);\n    Wb.log(Wb.payload); // text\n    Wb.log(Wb.payloadParams); // json\n  },\n  /**\n   * Process submit.\n   */\n  postSubmit() {\n    Wb.invoke('docs');\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module",
  "items": []
}

File name: client-request.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Client side web request",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "getTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "getTitle",
            "title": "Send GET request with parameters."
          }
        },
        {
          "_icon": "button",
          "text": "getBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "getBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=get',\n  params: { string: 'abc', int: 123, float: 123.4, date: new Date(), bool: true, object: { foo: 'bar' }, array: [1, 2, 3] },\n  method: 'GET',\n  success(resp) {\n    Wb.tipSucc(resp);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postTitle",
            "title": "Send POST request with parameters, files and blobs."
          }
        },
        {
          "_icon": "button",
          "text": "postBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=post',\n  // form: myHTMLForm,\n  // comps: [app.panel1, app.fileInput1],\n  // params: Wb.apply({ foo: 'bar' }, Wb.getValue(app.viewport1)),\n  params: { foo: 'bar' },\n  success(resp) {\n    Wb.tipSucc(resp);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "useFormTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "useFormTitle",
            "title": "Send POST request with large parameters"
          }
        },
        {
          "_icon": "button",
          "text": "useFormPostBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "useFormPostBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=post',\n  // Use multipart/form-data to support large parameter values,\n  // otherwise will result in failure, because application/x-www-form-urlencoded has length restrictions\n  form: true,\n  params: { foo: 'large text...' },\n  success(resp) {\n    Wb.tipSucc(resp);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "deserializeObjectTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "deserializeObjectTitle",
            "title": "Send request using object"
          }
        },
        {
          "_icon": "label",
          "text": "deserializeObjectLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "deserializeObjectLabel",
            "text": "It can serialize object before sending and auto deserialize all original values on the server side."
          }
        },
        {
          "_icon": "button",
          "text": "deserializeObjectBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "deserializeObjectBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=deserializeObject',\n  object: { string: 'abc', int: 123, float: 123.4, date: new Date(), bool: true, object: { foo: 'bar' }, array: [1, 2, 3] },\n  success() {\n    Wb.tipDone();\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postMultipleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postMultipleTitle",
            "title": "Post multiple parameters with the same name"
          }
        },
        {
          "_icon": "button",
          "text": "postMultipleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postMultipleBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postMultipleParams',\n  params: { myParam: Wb.markParams([1, 'abc', new Date()]) },\n  success() {\n    Wb.tipDone();\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postPayloadDataTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postPayloadDataTitle",
            "title": "Send request with payload data and specific header"
          }
        },
        {
          "_icon": "button",
          "text": "postPayloadDataBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postPayloadDataBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postPayload',\n  data: 'large text...',\n  header: { 'Content-Type': 'text/html;charset=utf-8' },\n  success() {\n    Wb.tipDone();\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postPayloadJsonTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postPayloadJsonTitle",
            "title": "Send request with payload json data"
          }
        },
        {
          "_icon": "button",
          "text": "postPayloadJsonBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postPayloadJsonBtn",
            "text": "Send request",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postPayloadJson',\n  data: { foo: 'abc', bar: 123 },\n  success() {\n    Wb.tipDone();\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "submitTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "submitTitle",
            "title": "Submit a regular HTTP request"
          }
        },
        {
          "_icon": "button",
          "text": "submitBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "submitBtn",
            "text": "Regular submit",
            "type": "primary"
          },
          "events": {
            "click": "Wb.submit(xpath + '/actions&xaction=postSubmit', { foo: 'abc', bar: 123 });"
          }
        },
        {
          "_icon": "title",
          "text": "browseTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "browseTitle",
            "title": "Browse url"
          }
        },
        {
          "_icon": "button",
          "text": "browseBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "browseBtn",
            "text": "Browse url",
            "type": "primary"
          },
          "events": {
            "click": "Wb.browse({ url: 'https://www.geejing.com', title: 'Geejing', icon: 'earth', params: { foo: 'abc', bar: 123 } });"
          }
        },
        {
          "_icon": "title",
          "text": "openWinTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "openWinTitle",
            "title": "Open window"
          }
        },
        {
          "_icon": "button",
          "text": "openWinBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "openWinBtn",
            "text": "Open window",
            "type": "primary"
          },
          "events": {
            "click": "Wb.openWin('https://www.geejing.com');\n//Wb.openWin({url: 'dbe', params: {foo: 'bar'}, method: 'POST'});"
          }
        }
      ]
    }
  ]
}

File name: code-static-load.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.set({ user: Wb.encode(Wb.execute('user')), dbe: Wb.encode(Wb.execute('admin/dbe')) });"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "events": {
        "ready": "// add user client\nWb.run({\n  script: _$user$_, success(scope) {\n    app.container1.add(scope.viewport1);\n  }\n});\n// add dbe client\nWb.run({\n  script: _$dbe$_, success(scope) {\n    app.container2.add(scope.viewport1);\n  }\n});"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "fit",
            "width": "50%"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "container",
          "text": "container2",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container2",
            "layout": "fit",
            "flex": "1"
          }
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Upload file.\n   */\n  upload() {\n    let singleFile, multiFiles;\n\n    singleFile = Params.singleFileInput;// same as Wb.get('singleFileInput');\n    //if singleFileInput uploaded\n    if (singleFile) {\n      Wb.log(singleFile.name + ' file size is: ' + singleFile.size);\n      //This example outputs the uploaded stream to a file, which you can also output to a database.\n      new Wb.File(true, 'temp/' + singleFile.name).stream = singleFile.stream;\n    }\n\n    //Whether uploading a single or multiple files, using getParams will result in an array like parameter list\n    multiFiles = Wb.getParams('multiFileInput');\n    multiFiles.forEach(file => {\n      Wb.log(file.name + ' file size is: ' + file.size);\n      new Wb.File(true, 'temp/' + file.name).stream = file.stream;\n    });\n\n    Wb.log('text1 value is: ' + Params.text1);\n\n    Wb.send(Base.pathText + 'temp');\n    //All streams will be automatically closed\n  },\n  /**\n   * Download wb.js file.\n   */\n  downloadWbJs() {\n    Wb.exportData(new Wb.File(true, 'wb/js/wb.js'));\n  },\n  /**\n   * Download a database blob.\n   */\n  downloadBlob() {\n    let row = Wb.getRow({ sql: \"select full_name, photo from wb_staff where sid='0040CCVTQ71JG'\", blob: true });\n\n    // row.photo is ByteArrayInputStream, no need to close\n    // row.photo.available is blob size, notify the client the length of the blob to display the download progress on the client\n    Wb.exportData(row.photo, row.full_name + '.png', row.photo.available())\n  },\n  /**\n   * Download a zipped css folder file.\n   */\n  downloadZip() {\n    Wb.setContentType('css.zip');\n    ZipUtil.zip([new File(Base.path, 'wb/css')], response.getOutputStream());\n  },\n  /**\n   * Download after upload.\n   */\n  downloadAfterUpload() {\n    let multiFiles = Wb.getParams('multiFileInput');\n    multiFiles.forEach(file => {\n      Wb.log(file.name + ' file size is: ' + file.size);\n      new Wb.File(true, 'temp/' + file.name).stream = file.stream;\n    });\n    Wb.exportData(new Wb.File(true, 'wb/js/wb.js'));\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: file-updownload.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "File upload & download",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "uploadTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "uploadTitle",
            "title": "Upload files"
          }
        },
        {
          "_icon": "container",
          "text": "uploadCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "uploadCt",
            "layout": "grid1",
            "frame": "true",
            "labelUp": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "search-file",
              "text": "singleFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "singleFileInput",
                "text": "Please select a file"
              }
            },
            {
              "_icon": "search-file",
              "text": "multiFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "multiFileInput",
                "browseMode": "true",
                "text": "Please select multiple files",
                "multiple": "true",
                "accept": "image/*",
                "browseHeight": "6em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Other control"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "uploadButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "uploadButton",
                "text": "Upload all",
                "type": "primary"
              },
              "events": {
                "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=upload',\n  comps: app.uploadCt,\n  params: { foo: 'bar' },\n  // params: Wb.getValue(app.uploadCt), // same as comps property\n  success(resp) {\n    Wb.tipSucc('All files have been saved to \"' + resp + '\".');\n  }\n});\n\n//Please also refer to Wb.fetch the promise version of wb.ajax "
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "downloadTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "downloadTitle",
            "title": "Download files"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "downloadCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "downloadCt",
            "layout": "grid1",
            "frame": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "button",
              "text": "commonDownloadWbJsButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "commonDownloadWbJsButton",
                "text": "Common download wb.js",
                "type": "primary"
              },
              "events": {
                "click": "//Using Wb.download is the recommended method for downloading\nWb.download(xpath + '/actions&xaction=downloadWbJs', { foo: 'bar' });"
              }
            },
            {
              "_icon": "button",
              "text": "ajaxDownloadWbJsButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "ajaxDownloadWbJsButton",
                "text": "Ajax download wb.js",
                "type": "primary"
              },
              "events": {
                "click": "//Using Wb.ajax is usually not a recommended method for downloading, please use Wb.download instead\nWb.ajax({\n  url: xpath + '/actions&xaction=downloadWbJs',\n  download: true,\n  params: { foo: 'bar' },\n  onDownload(percent) {\n    Wb.progress(percent);\n  }\n});"
              }
            },
            {
              "_icon": "button",
              "text": "downloadBlobButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "downloadBlobButton",
                "text": "Download blob",
                "type": "primary"
              },
              "events": {
                "click": "Wb.download(xpath + '/actions&xaction=downloadBlob');"
              }
            },
            {
              "_icon": "button",
              "text": "downloadZipButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "downloadZipButton",
                "text": "Download zip",
                "type": "primary"
              },
              "events": {
                "click": "Wb.download(xpath + '/actions&xaction=downloadZip');"
              }
            },
            {
              "_icon": "button",
              "text": "downloadBase64BlobButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "downloadBase64BlobButton",
                "text": "Download base64/blob",
                "type": "primary"
              },
              "events": {
                "click": "let base64Text = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAJlJREFUOE/FkjsOgzAQBWfLEOUIqbgInCrQmQpuFXMmFNpFJiTCfCIMSHG9nn1vtMLBJwf/0wNeJWYP6JpjekBb8QSSEIgqxXmApgzb/kl6y7GeRFclykhDqkwBGmVvsSM3izxVas+BCHdVYsC6Hy5JW6FraTyJo43TBKsAEczlQfGzwhYX51zi/w9pSLCl8ndGBDuTGEQYhjv9pkQRnIOeTgAAAABJRU5ErkJggg==';\nWb.downloadBase64(base64Text, 'insert.png');\n//Wb.downloadBlob(fileOrBlob, 'myFile.dat');"
              }
            }
          ]
        },
        {
          "_icon": "title",
          "text": "downloadAfterUploadingTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "downloadAfterUploadingTitle",
            "title": "Download after uploading"
          }
        },
        {
          "_icon": "container",
          "text": "downloadAfterUploadingCt",
          "cls": "Wb.Container",
          "properties": {
            "cid": "downloadAfterUploadingCt",
            "layout": "grid1",
            "frame": "true",
            "labelUp": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "search-file",
              "text": "multiFileInput",
              "cls": "Wb.FileInput",
              "properties": {
                "cid": "multiFileInput",
                "browseMode": "true",
                "text": "Please select multiple files",
                "multiple": "true",
                "accept": "image/*",
                "browseHeight": "6em"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "text",
              "text": "text1",
              "cls": "Wb.Text",
              "properties": {
                "cid": "text1",
                "text": "Other control"
              },
              "hasDupCid": 1
            },
            {
              "_icon": "button",
              "text": "downloadAfterUploadButton",
              "cls": "Wb.Button",
              "properties": {
                "cid": "downloadAfterUploadButton",
                "text": "Download after upload",
                "type": "primary"
              },
              "events": {
                "click": "const uploading = 'Uploading now, please wait...';\nconst downloading = 'Downloading now, please wait...';\n\nWb.ajax({\n  url: xpath + '/actions&xaction=downloadAfterUpload',\n  comps: app.downloadAfterUploadingCt,\n  download: true,\n  params: { foo: 'bar' },\n  mask: uploading, //let the default mask is \"uploading\"\n  onUpload(percent) {\n    Wb.progress(percent, uploading);\n  },\n  onDownload(percent) {\n    Wb.progress(percent, downloading);\n  },\n  callback() {\n    Wb.unmask(downloading);\n  }\n});"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: execute.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Load a javascript library.\n   */\n  loadJs() {\n    let result;\n\n    Wb.load('./util.js');\n    result = My.Util.add(Wb.getInt('value1'), Wb.getInt('value2'));\n    Wb.send(result);\n  },\n  /**\n   * Synchronous load a javascript module.\n   */\n  loadMjs() {\n    let result, Util = Wb.load('./util.mjs');\n\n    result = Util.add(Wb.getInt('value1'), Wb.getInt('value2'));\n    Wb.send(result);\n  },\n  /**\n   * Asynchronous import a javascript module. This method is not recommended, please use Wb.load instead.\n   */\n  importMjs() {\n    import('wb/modules/example/load/server-load-module/util.mjs').then(m => {\n      let result = m.default.add(Wb.getInt('value1'), Wb.getInt('value2'));\n      Wb.send(result);\n    }).catch(e => {\n      Wb.error(e?.toString());\n    });\n  },\n  /**\n   * Run a xwl module serverscript function.\n   */\n  runXwl() {\n    let result;\n\n    Wb.run('./util.xwl');\n    result = app.add(Wb.getInt('value1'), Wb.getInt('value2'));\n    Wb.send(result);\n  },\n  /**\n   * Invoke a xwl module execute serverscript and obtain client script.\n   */\n  invokeXwl() {\n    let script = Wb.execute('./query');\n    Wb.send(script);\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: query.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.sendRows('select * from wb_staff');"
  },
  "_icon": "module"
}

File name: util.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.apply(app, {\n  /**\n   * Perform add.\n   * @param {Number} value1 Value 1\n   * @param {Number} value2 Value 2\n   * @return {Number} The result\n   */\n  add(value1, value2) {\n    return value1 + value2;\n  }\n});\nWb.log('Util.xwl runs every time.');"
  },
  "_icon": "module"
}

File name: server-load-module.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Server load module, js and mjs",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "runModuleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "runModuleTitle",
            "title": "Run module"
          }
        },
        {
          "_icon": "label",
          "text": "runModuleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "runModuleLabel",
            "text": "Run the module at serverside and call function of the module."
          }
        },
        {
          "_icon": "button",
          "text": "runModuleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "runModuleBtn",
            "text": "Run module",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/execute&xaction=runXwl',\n  params: { value1: 12, value2: 34 },\n  success(resp) {\n    Wb.tip('The result is ' + resp);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "invokeModuleTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "invokeModuleTitle",
            "title": "Invoke module"
          }
        },
        {
          "_icon": "label",
          "text": "InvokeModuleLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "InvokeModuleLabel",
            "text": "Call the module and obtain the HTML or script returned by the module."
          }
        },
        {
          "_icon": "button",
          "text": "invokeModuleBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "invokeModuleBtn",
            "text": "Invoke module",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/execute&xaction=invokeXwl',\n  success(resp) {\n    app.invokeModuleViewer.text = resp;\n    console.table(Wb.decode(resp));\n  }\n});"
          }
        },
        {
          "_icon": "desktop",
          "text": "invokeModuleViewer",
          "cls": "Wb.Viewer",
          "properties": {
            "cid": "invokeModuleViewer",
            "height": "10em"
          }
        },
        {
          "_icon": "title",
          "text": "loadJSTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "loadJSTitle",
            "title": "Load Javascript"
          }
        },
        {
          "_icon": "label",
          "text": "loadJSLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "loadJSLabel",
            "text": "Load Javascript library at serverside."
          }
        },
        {
          "_icon": "button",
          "text": "loadJSBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "loadJSBtn",
            "text": "Load js",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/execute&xaction=loadJs',\n  params: { value1: 12, value2: 34 },\n  success(resp) {\n    Wb.tip('The result is ' + resp);\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "loadMJSTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "loadMJSTitle",
            "title": "Load mjs"
          }
        },
        {
          "_icon": "label",
          "text": "loadMJSLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "loadMJSLabel",
            "text": "Load Javascript module at serverside by Wb.load(recommended)."
          }
        },
        {
          "_icon": "button",
          "text": "loadMJSBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "loadMJSBtn",
            "text": "Load mjs",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/execute&xaction=loadMjs',\n  params: { value1: 12, value2: 34 },\n  success(resp) {\n    Wb.tip('The result is ' + resp);\n  }\n});"
          }
        },
        {
          "_icon": "label",
          "text": "importMJSLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "importMJSLabel",
            "text": "Import Javascript module at serverside by import(not recommended)."
          }
        },
        {
          "_icon": "button",
          "text": "importMJSBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "importMJSBtn",
            "text": "Import mjs",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/execute&xaction=importMjs',\n  params: { value1: 12, value2: 34 },\n  success(resp) {\n    Wb.tip('The result is ' + resp);\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Send get request.\n   */\n  getHtml() {\n    Wb.send(Wb.submit('https://developer.mozilla.org'));\n  },\n  /**\n   * Send post request with params.\n   */\n  postParams() {\n    // For reference \n    // Wb.submit({ url: 'http://localhost:8080/wb/m?xwl=test2', params: { foo: 'bar' } });\n  },\n  /**\n   * Send post request with payload data.\n   */\n  postPayload() {\n    // For reference \n    // Wb.submit({ url: 'http://localhost:8080/wb/m?xwl=test2', data: payloadObject });\n  },\n  /**\n   * Send post request with files.\n   */\n  postFiles() {\n    // Wb.submit({\n    //   url: 'http://localhost:8080/wb/m?xwl=test2',\n    //   params: {\n    //     foo: 'bar', 'wb.js': new Wb.File(true, 'wb/js/wb.js'), file: javaFile,\n    //     myBytes: bytes, 'name|filename.dat': inputStream\n    //   }\n    // });\n  },\n  /**\n   * Login to the system, verify the username and password, and get the cookie token.\n   */\n  loginAndLogout() {\n    // For reference \n    /*\n    let cookie = Wb.submit({\n      url: 'http://localhost:8080/wb/verify', all: true, params: { username: 'admin', password: 'admin' }\n    }).cookie;\n    //Access module1 with logined cookie token\n    let result = Wb.submit({ url: 'http://localhost:8080/wb/m?xwl=module1', cookie }, { foo: 'bar' });\n    //Logout\n    Wb.submit({ url: 'http://localhost:8080/wb/logout', cookie });\n    */\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module",
  "items": []
}

File name: server-request.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "Server side web request",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "getHtmlTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "getHtmlTitle",
            "title": "Get mozilla.org site html"
          }
        },
        {
          "_icon": "desktop",
          "text": "htmlSourceViewer",
          "cls": "Wb.Viewer",
          "properties": {
            "cid": "htmlSourceViewer",
            "height": "8em"
          }
        },
        {
          "_icon": "button",
          "text": "getHtmlBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "getHtmlBtn",
            "text": "Get html",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=getHtml',\n  success(resp) {\n    app.htmlSourceViewer.text = resp;\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postParamsTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postParamsTitle",
            "title": "Send post request with params"
          }
        },
        {
          "_icon": "button",
          "text": "postParamsBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postParamsBtn",
            "text": "Post params",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postParams',\n  success(resp) {\n    Wb.tipDone();\n  }\n});"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "postPayloadDataTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postPayloadDataTitle",
            "title": "Send post request with payload data"
          }
        },
        {
          "_icon": "button",
          "text": "postPayloadDataBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postPayloadDataBtn",
            "text": "Post payload data",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postPayload',\n  success(resp) {\n    Wb.tipDone();\n  }\n});"
          }
        },
        {
          "_icon": "title",
          "text": "postFileTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "postFileTitle",
            "title": "Send post request with files"
          }
        },
        {
          "_icon": "button",
          "text": "postFilesBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "postFilesBtn",
            "text": "Post files",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=postFiles',\n  success(resp) {\n    Wb.tipDone();\n  }\n});"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "loginTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "loginTitle",
            "title": "Login to the system, access modules and logout."
          }
        },
        {
          "_icon": "button",
          "text": "loginAccessBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "loginAccessBtn",
            "text": "Login and Access",
            "type": "primary"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=loginAndLogout',\n  success(resp) {\n    Wb.tipDone();\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: switch-module.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.invoke(Params.type == 'someType' ? 'admin/user' : 'dbe');"
  },
  "_icon": "module"
}

File name: xwl-static-load.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/js/monaco.js\"\n]",
    "description": "Please note that since the xwl module can be recursively loaded, \nits dependent js/css resources need to be explicitly loaded in the module's links property"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "items": [
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "fit",
            "width": "50%"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "module",
              "text": "userXwl",
              "cls": "Wb.Xwl",
              "properties": {
                "cid": "userXwl",
                "path": "admin/user.xwl",
                "ready": "// parentApp in user.xwl is app of this module\n// parentContainer in user.xwl is Wb.Container of the user.xwl wrapper\nscope.viewport1.title = 'User Module'; // same as app.userXwl.viewport1.title = 'User Module';\ncontainer.border = 1;",
                "container": "({ padding: '.5em' })"
              }
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          },
          "_expanded": true
        },
        {
          "_icon": "container",
          "text": "container2",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container2",
            "layout": "fit",
            "flex": "1"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "module",
              "text": "dbeXwl",
              "cls": "Wb.Xwl",
              "properties": {
                "cid": "dbeXwl",
                "path": "admin/dbe.xwl",
                "ready": "scope.viewport1.title = 'DBE Module';\ncontainer.border = 1;",
                "container": "({ padding: '.5em' })"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Select data.\n   */\n  select() {\n    Wb.sendDict('select * from wb_staff', 'wb,');\n  },\n  /**\n   * Get form.\n   */\n  getForm() {\n    Wb.send(Wb.getExcelHtml('form.xlsx', { now: new Date().dateText }));\n  },\n  /**\n   * Export data.\n   */\n  export() {\n    let data = Wb.getObject('data');\n    data.gender = data.gender$; // display text\n    Wb.getExcelFile('my-form.xlsx', 'form.xlsx', data);\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: excel-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Fire after module is loaded.\n   */\n  onLoad() {\n    Wb.ajax({\n      url: xpath + '/actions&xaction=getForm',\n      success(resp) {\n        app.container1.html = resp;\n        Wb.setValue(app.container1, { name: 'Your name', code: '10000' });\n      }\n    });\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "array",
      "text": "bindComps",
      "cls": "Wb.Array",
      "properties": {
        "cid": "bindComps"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "text",
          "text": "name",
          "cls": "Wb.Text",
          "properties": {
            "cid": "name",
            "required": "true",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "nation",
          "cls": "Wb.Text",
          "properties": {
            "cid": "nation",
            "instanced": "false"
          }
        },
        {
          "_icon": "combo",
          "text": "gender",
          "cls": "Wb.Select",
          "properties": {
            "cid": "gender",
            "keyName": "gender",
            "instanced": "false"
          }
        },
        {
          "_icon": "calendar",
          "text": "birthdate",
          "cls": "Wb.Date",
          "properties": {
            "cid": "birthdate",
            "instanced": "false"
          }
        },
        {
          "_icon": "number-edit",
          "text": "height",
          "cls": "Wb.Number",
          "properties": {
            "cid": "height",
            "instanced": "false",
            "decimalCount": "1"
          }
        },
        {
          "_icon": "number-edit",
          "text": "weight",
          "cls": "Wb.Number",
          "properties": {
            "cid": "weight",
            "instanced": "false"
          }
        },
        {
          "_icon": "number-edit",
          "text": "salary",
          "cls": "Wb.Number",
          "properties": {
            "cid": "salary",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "marry",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "marry",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "mobile",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "mobile",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "email",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "email",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "address",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "address",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "contact",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "contact",
            "instanced": "false"
          }
        },
        {
          "_icon": "image",
          "text": "photo",
          "cls": "Wb.Image",
          "properties": {
            "cid": "photo",
            "src": "wb/images/app/logo.png",
            "instanced": "false",
            "height": "140"
          }
        },
        {
          "_icon": "text",
          "text": "university",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "university",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "grade",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "grade",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "speciality",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "speciality",
            "instanced": "false"
          }
        },
        {
          "_icon": "text",
          "text": "class",
          "cls": "Wb.Text",
          "_expanded": true,
          "properties": {
            "cid": "class",
            "instanced": "false"
          }
        },
        {
          "_icon": "grid",
          "text": "grid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid",
            "url": "@xpath + '/actions&xaction=select'",
            "instanced": "false",
            "height": "169"
          }
        },
        {
          "_icon": "textarea",
          "text": "resume",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "resume",
            "instanced": "false",
            "height": "186"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "autoScroll": "true",
        "layout": "fit"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "exportItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "exportItem",
                "text": "Export",
                "icon": "export"
              },
              "events": {
                "click": "Wb.download(xpath + '/actions&xaction=export', { data: Wb.getValue(app.container1) });"
              }
            }
          ]
        },
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "middle",
            "autoMake": "true"
          }
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Reload data.\n   */\n  reload() {\n    let html;\n\n    html = Wb.getExcelHtml('report.xlsx', this.getData(), this.getRowConfigs());\n    Wb.send(html);\n  },\n  /**\n   * Export data.\n   */\n  export() {\n    Wb.getExcelFile('my-report.xlsx', 'report.xlsx', this.getData(), this.getRowConfigs());\n  },\n  /**\n   * Get report data.\n   * @return {Object} Report data.\n   */\n  getData() {\n    return {\n      name: 'Geejing', date: new Date().dateText, by: 'Janice Galvin', reviewer: 'Rachel Valdez',\n      director: 'Annette Hill', email: 'contact@geejing.com',\n      rows: Wb.getAllRows('select * from wb_report_demo1 order by row_num')\n    };\n  },\n  /**\n   * Get write rows configs. @priv\n   */\n  getRowConfigs() {\n    // [{ name: 'rows', x: 0, y: 5, sheet: 'Sheet2', mergeRows: ['f1', 'f3'], mergeCols: [['f1', 'f2'], ['f5', 'f6']] }, more...]\n    return {\n      name: 'rows', x: 0, y: 5, mergeRows: 'f1', mergeCols: 'f1, f2'\n    }\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: excel-report.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Fire after module is loaded.\n   */\n  onLoad() {\n    app.reloadItem.fireEvent('click');\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "autoScroll": "true",
        "layout": "fit"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "reloadItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "reloadItem",
                "text": "Reload",
                "icon": "refresh"
              },
              "events": {
                "click": "Wb.ajax({\n  url: xpath + '/actions&xaction=reload',\n  success(resp) {\n    app.container1.html = '';\n    app.container1.html = resp;\n  }\n});"
              }
            },
            {
              "_icon": "item",
              "text": "exportItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "exportItem",
                "text": "Export",
                "icon": "export"
              },
              "events": {
                "click": "Wb.download(xpath + '/actions&xaction=export');"
              }
            },
            {
              "_icon": "item",
              "text": "printItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "printItem",
                "text": "Print",
                "icon": "print"
              },
              "events": {
                "click": "Wb.print(app.container1, 'A3 landscape');"
              }
            }
          ]
        },
        {
          "_icon": "container",
          "text": "container1",
          "cls": "Wb.Container",
          "properties": {
            "cid": "container1",
            "layout": "middle",
            "cls": "w-max-size /* for print centered only */"
          }
        }
      ]
    }
  ]
}

File name: send.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let sender, attachFiles, submitFiles = [];\n\n  sender = new Wb.MailSender(\n    Params.smtp,\n    Params.username,\n    Params.password,\n    Params.authUsername,\n    Params.authPassword,\n    { 'mail.smtp.auth': true, 'mail.smtp.sendpartial': true }\n  );\n  attachFiles = Wb.getParams('attachFiles');\n  attachFiles.forEach(file => {\n    submitFiles.push({ name: file.name, data: file.stream });\n  });\n  try {\n    sender.send(Params.from, Params.to, Params.title, Params.content, submitFiles, Params.cc);\n  } finally {\n    sender.close();\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: mail-sender.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "title",
          "text": "titleComp",
          "cls": "Wb.Title",
          "properties": {
            "cid": "titleComp",
            "title": "Email Sender"
          }
        },
        {
          "_icon": "text",
          "text": "smtp",
          "cls": "Wb.Text",
          "properties": {
            "cid": "smtp",
            "text": "Smtp",
            "required": "true",
            "value": "smtp.xxx.com"
          }
        },
        {
          "_icon": "text",
          "text": "username",
          "cls": "Wb.Text",
          "properties": {
            "cid": "username",
            "text": "Username",
            "required": "true",
            "value": "name@xxx.com"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "password",
          "cls": "Wb.Text",
          "properties": {
            "cid": "password",
            "text": "Password",
            "required": "true",
            "value": "password"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "authUsername",
          "cls": "Wb.Text",
          "properties": {
            "cid": "authUsername",
            "text": "Auth username",
            "value": "name@xxx.com"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "authPassword",
          "cls": "Wb.Text",
          "properties": {
            "cid": "authPassword",
            "text": "Auth password",
            "value": "someAuthNumber"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "from",
          "cls": "Wb.Text",
          "properties": {
            "cid": "from",
            "text": "From",
            "required": "true",
            "value": "name@xxx.com"
          }
        },
        {
          "_icon": "text",
          "text": "to",
          "cls": "Wb.Text",
          "properties": {
            "cid": "to",
            "text": "To",
            "required": "true",
            "value": "to@xxx.com"
          }
        },
        {
          "_icon": "text",
          "text": "cc",
          "cls": "Wb.Text",
          "properties": {
            "cid": "cc",
            "text": "CC"
          }
        },
        {
          "_icon": "text",
          "text": "title",
          "cls": "Wb.Text",
          "properties": {
            "cid": "title",
            "text": "Title"
          }
        },
        {
          "_icon": "textarea",
          "text": "content",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "content",
            "text": "Mail Content",
            "value": "Hello world"
          }
        },
        {
          "_icon": "search-file",
          "text": "attachFiles",
          "cls": "Wb.FileInput",
          "properties": {
            "cid": "attachFiles",
            "multiple": "true",
            "text": "Attach Files"
          }
        },
        {
          "_icon": "button",
          "text": "Send",
          "cls": "Wb.Button",
          "properties": {
            "cid": "Send",
            "text": "Send",
            "icon": "paper-plane",
            "type": "primary"
          },
          "events": {
            "click": "if (!Wb.verify(app.viewport1))\n  return;\nWb.ajax({\n  url: xpath + '/send',\n  comps: app.viewport1,\n  success() {\n    Wb.tipSucc('The email has been successfully sent.');\n  }\n});"
          }
        }
      ]
    }
  ]
}

File name: create.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let bos, rowx, cols;\n\n  bos = new ByteArrayOutputStream();\n  rowx = Wb.getRowx({ sql: 'select * from wb_staff', rs: -1 });\n  cols = rowx.columns;\n  cols.shift(); // shift row num col\n  cols.forEach(col => col.width = parseInt(col.width));\n  com.wb.office.SheetWriter.createExcel(bos, Wb.toJava(cols), Wb.toJava(rowx.items), 0, 'Staff List', null);\n  Wb.exportData(bos.toByteArray(), 'staff.xlsx');\n}\nmain();"
  },
  "_icon": "module"
}

File name: sql-to-excel.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "label",
          "text": "mainLabel",
          "cls": "Wb.Label",
          "properties": {
            "cid": "mainLabel",
            "text": "SQL to Excel",
            "cls": "w-title3"
          }
        },
        {
          "_icon": "title",
          "text": "textTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "textTitle",
            "title": "Export to Excel"
          }
        },
        {
          "_icon": "button",
          "text": "downloadBtn",
          "cls": "Wb.Button",
          "properties": {
            "cid": "downloadBtn",
            "text": "Download",
            "type": "primary"
          },
          "events": {
            "click": "Wb.download(xpath + '/create');"
          }
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Get chats list. @priv\n   */\n  getChats() {\n    Wb.sendRowx({\n      sql: `\n      select a.send_date, a.text_content, a.bin_len, b.userid, c.display_name from\n      \n      wb_chats a,\n\n      (\n        select userid, max(sid) as sid from \n        (\n          select sid,from_user as userid from wb_chats where to_user={?sys.userid?}\n          union all\n          select sid,to_user as userid  from wb_chats where from_user={?sys.userid?}\n        ) x\n        group by userid\n      ) b,\n\n      wb_user c\n\n      where a.sid=b.sid and b.userid=c.sid\n      order by a.send_date desc\n    `,\n      clob: 100\n    });\n  },\n  /**\n   * Get chat detail. @priv\n   */\n  getChatDetail() {\n    Wb.sendRows(`\n      select sid,send_date,text_content,bin_len,1 as direction from wb_chats where to_user={?sys.userid?} and from_user={?userid?}\n      union all\n      select sid,send_date,text_content,bin_len,0 as direction from wb_chats where from_user={?sys.userid?} and to_user={?userid?}\n      order by send_date desc\n    `);\n  },\n  /**\n   * Download the chat blob content. @priv\n   */\n  download() {\n    let row = Wb.getRow({ sql: 'select bin_len,text_content,bin_content from wb_chats where sid={?id?}', blob: true });\n    // for download filename purpose\n    Wb.setContentType(row.text_content, null, row.bin_len);\n    Wb.send(row.bin_content);\n  },\n  /**\n   * Get contact user list. @priv\n   */\n  getContacts() {\n    Wb.sendRowx('select sid, user_name, display_name, email from wb_user where sid<>{?sys.userid?} and status=1');\n  },\n  /**\n   * Send message from current user to another user. @priv\n   */\n  sendMessage() {\n    let file = Params.file, toUser = Params.userid, data;\n\n    data = {\n      sid: Wb.getId(),\n      send_date: new Date(),\n      from_user: Wb.userid,\n      bin_len: file?.size,\n      text_content: Params.text\n    }\n    Wb.sync({\n      tableName: 'wb_chats',\n      insert: Wb.apply({ to_user: toUser, bin_content: file?.stream }, data)\n    });\n    data.display_name = Wb.dispname;\n    Wb.send(data, 'wb.chat', toUser);\n    Wb.send(data);\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: chat.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "links": "[\n  \"wb/css/demo.css\"\n]"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {Array} - Cached url resource list. @priv */\n  cachedUrls: [],\n  /** @property {Object} chatData The user data of current chat dialog. @priv */\n  /**\n   * View chat details of the specific user. @priv\n   * @param {Object} data The user data.\n   * @param {String} .userid The user id.\n   * @param {String} .display_name The user display name.\n   * @param {Function} [callback] A function to be called when the chat window is displayed.\n   */\n  openChat(data, callback) {\n    let userid = data.userid;\n    Wb.ajax({\n      url: xpath + '/actions&xaction=getChatDetail',\n      params: { userid },\n      json: true,\n      success(resp) {\n        let detailComp = app.detailComp, dialog = app.chatDetailDialog;\n        detailComp.el.textContent = '';\n        app.cachedUrls.forEach(item => URL.revokeObjectURL(item));\n        app.lastChatDate = null;\n        resp.each(item => {\n          app.addMessage(item, false, true);\n        }, true, true);\n        dialog.title = data.display_name;\n        dialog.setVisible(true, f => callback?.call(dialog));\n        app.chatData = data;\n        detailComp.dialogScrollTop ??= {};\n        detailComp.el.scrollTop = detailComp?.dialogScrollTop[userid] ?? detailComp.el.scrollHeight;\n        dialog.on('close', f => detailComp.dialogScrollTop[userid] = detailComp.el.scrollTop, true, { once: true });\n        app.chatsView.findItem('userid', userid)?.set('badge', null);\n      }\n    });\n  },\n  /**\n   * Create chat message in chat dialog. @priv\n   * @param {Object} data Chat data.\n   * @param {Boolean} [loadingIcon] Whether to create a loading icon.\n   * @param {Boolean} [keepScrollPos] Whether to keep scroll position.\n   * @return {Object} {loadingEl, textEl} object.\n   */\n  addMessage(data, loadingIcon, keepScrollPos) {\n    let detailComp = app.detailComp, el = detailComp.el, date, wrap, textEl, loadingEl;\n\n    date = data.send_date.dateValue;\n    //if span > 10 min\n    if (!app.lastChatDate || date.elapse(app.lastChatDate) > 600000)\n      el.addEl('w-title5 w-ta-center').textContent = date.prettyText;\n    wrap = el.addEl('w-row w-align-center w-gap w-margin-tb1');\n    if (data.direction == 1) {\n      wrap.addEl('w-icon icon-user2 w-size2');\n      // you can merge all classes to one class\n      app.createContent(textEl = wrap.addEl(), data);\n    } else {\n      wrap.cls = 'w-justify-end';\n      if (loadingIcon)\n        loadingEl = wrap.addEl('w-icon w-spin icon-loading w-hidden');\n      app.createContent(textEl = wrap.addEl(), data);\n      wrap.addEl('w-icon icon-user3 w-size2');\n    }\n    app.lastChatDate = date;\n    if (!keepScrollPos)\n      detailComp.scrollToBottom();\n    return { loadingEl, textEl };\n  },\n  /**\n   * Create message content. @priv\n   * @param {Element} el The message element.\n   * @param {Object} data The data to render.\n   */\n  createContent(el, data) {\n    let text = data.text_content, size = data.bin_len, sid = data.sid;\n    if (size != null) {\n      if (Wb.isImageFile(text)) {\n        let img = el.addEl('w-pointer', 'img'), url;\n\n        if (data.file) {\n          url = URL.createObjectURL(data.file);\n          app.cachedUrls.push(url);\n        } else {\n          url = xpath + '/actions&xaction=download&id=' + sid;\n        }\n        img.draggable = false;\n        img.src = url;\n        img.height = 80;\n      } else {\n        let wrap = el.addEl('w-column');\n        el.cls = 'w-btn w-row w-align-center w-gap2 wx-file';\n        wrap.addEl('w-label').textContent = text;\n        wrap.addEl('w-title5').textContent = size.fileSize;\n        el.addEl('w-size2 w-icon icon-' + Wb.getFileIcon(text));\n        if (sid)\n          el.recordSid = sid;\n        el.maxWidth = '70%';\n      }\n    } else {\n      el.cls = data.direction == 1 ? 'w-msg1' : 'w-msg2';\n      el.textContent = text;\n    }\n  },\n  /** @property {Object} openUserData The user data of current user dialog. @priv */\n  /**\n   * View user details of the specific user. @priv\n   * @param {Object} data The user data to open.\n   */\n  openUser(data) {\n    let dialog = app.userDetailDialog;\n    app.summaryComp.update(data)\n    app.openUserData = data;\n    dialog.show();\n    dialog.el.scrollTop = 0;\n  },\n  /**\n   * Send text message. @priv\n   */\n  sendText() {\n    let textArea = app.textArea1, value = textArea.value;\n    if (value.length) {\n      app.doSend({ text_content: value });\n      textArea.clear();\n    } else {\n      Wb.tipWarn('Please input text');\n      textArea.focus();\n    }\n  },\n  /**\n   * Send file. @priv\n   */\n  sendFile() {\n    Wb.selectFile(file => {\n      app.doSend({ text_content: file.name, file, bin_len: file.size });\n    });\n  },\n  /**\n   * Send any data. @priv\n   * @param {Object} [configs] Configs data.\n   */\n  doSend(configs) {\n    let data = app.chatData, loadingIcon, elMap, userid = data.userid;\n\n    elMap = app.addMessage(Wb.apply({\n      send_date: new Date().textValue,\n      direction: 2\n    }, configs), true);\n    loadingIcon = elMap.loadingEl;\n    loadingIcon.removeCls.delay(loadingIcon, Wb.configs.maskDelay, 'w-hidden');\n    Wb.ajax({\n      url: xpath + '/actions&xaction=sendMessage',\n      params: { userid, text: configs.text_content, file: configs.file },\n      mask: false,\n      json: true,\n      callback(resp, xhr) {\n        loadingIcon.removeCls.cancelDelay();\n        if (xhr.ok)\n          loadingIcon.remove();\n        else\n          loadingIcon.className = 'w-icon icon-error';\n        elMap.textEl.recordSid = resp.sid;\n        app.updateChatView({\n          display_name: data.display_name,\n          userid,\n          send_date: resp.send_date,\n          text_content: resp.text_content\n        });\n      }\n    });\n  },\n  /**\n   * Update chat view list when sending or receiving messages\n   * @param {Object} [data] Chat view item data.\n   * @param {Boolean} [incBadge] Increment badge count.\n   */\n  updateChatView(data, incBadge) {\n    let chatItem, chatsView = app.chatsView;\n\n    chatItem = chatsView.findItem('userid', data.userid) ?? new Wb.ViewItem();\n    chatsView.insert(0, chatItem);\n    if (incBadge)\n      data.badge = Math.min(99, (chatItem.data.badge || 0) + 1);\n    chatItem.update(data);\n  },\n  /**\n   * Receive message from websocket. @priv\n   */\n  receiveMessage(e) {\n    let data = Wb.decode(e.data);\n\n    // add to chat detail dialog\n    if (data.from_user == app.chatData?.userid) {\n      data.direction = 1;\n      app.addMessage(data);\n    } else {\n      app.updateChatView({\n        display_name: data.display_name,\n        userid: data.from_user,\n        send_date: data.send_date,\n        text_content: data.text_content\n      }, true);\n    }\n  }\n});"
  },
  "items": [
    {
      "_icon": "plug",
      "text": "socket",
      "cls": "Wb.Socket",
      "properties": {
        "cid": "socket",
        "name": "wb.chat"
      },
      "events": {
        "close": "if (!Wb.unloading && !Globals.WbMainTab)\n  Wb.login();",
        "message": "app.receiveMessage(e);"
      }
    },
    {
      "_icon": "array",
      "text": "templates",
      "cls": "Wb.Array",
      "properties": {
        "cid": "templates"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "dialog",
          "text": "chatDetailDialog",
          "cls": "Wb.Dialog",
          "properties": {
            "cid": "chatDetailDialog",
            "layout": "column",
            "background": "true",
            "focusControl": "false"
          },
          "_expanded": true,
          "events": {
            "close": "app.chatData = null;"
          },
          "items": [
            {
              "_icon": "component",
              "text": "detailComp",
              "cls": "Wb.Component",
              "properties": {
                "cid": "detailComp",
                "flex": "1",
                "border": "true",
                "cls": "w-scroll-y w-padding-lr1"
              },
              "events": {
                "ready": "this.onClickPreview();",
                "click": "let fileBtn = e.target.closest('.wx-file');\nif (fileBtn) {\n  let sid = fileBtn.recordSid;\n  if (sid) {\n    Wb.download(xpath + '/actions&xaction=download&id=' + sid);\n  } else {\n    Wb.tip('The file is being sent, please wait...');\n  }\n}"
              }
            },
            {
              "_icon": "splitter",
              "text": "splitter1",
              "cls": "Wb.Splitter",
              "properties": {
                "cid": "splitter1",
                "visible": "@Wb.isDesktop"
              }
            },
            {
              "_icon": "panel",
              "text": "panel1",
              "cls": "Wb.Panel",
              "properties": {
                "cid": "panel1",
                "layout": "row",
                "padding": "@Wb.isTouch",
                "border": "false"
              },
              "_expanded": true,
              "items": [
                {
                  "cls": "Wb.Toolbar",
                  "properties": {
                    "cid": "tbar",
                    "isProperty": "true",
                    "background": "true",
                    "visible": "@Wb.isDesktop",
                    "border": "false",
                    "plainIcon": "true"
                  },
                  "text": "tbar",
                  "_expanded": true,
                  "_icon": "toolbar",
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "fileItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "fileItem",
                        "tip": "Send file",
                        "icon": "folder-open",
                        "handler": "app.sendFile"
                      },
                      "hasDupCid": 1
                    }
                  ]
                },
                {
                  "_icon": "textarea",
                  "text": "textArea1",
                  "cls": "Wb.TextArea",
                  "properties": {
                    "cid": "textArea1",
                    "maxHeight": "@Wb.isTouch?'10em':undefined",
                    "autoResize": "@Wb.isTouch",
                    "rows": "1",
                    "minHeight": "@Wb.isTouch?undefined:'8em'",
                    "flex": "1",
                    "inactiveBorder": "true",
                    "padding": "true"
                  },
                  "_expanded": true,
                  "events": {
                    "keydown": "if (e.isGoKey) {\n  if (e.ctrlMeta)\n    this.replaceSelection('\\n');\n  else\n    app.sendText();\n  e.stopEvent();\n}"
                  }
                },
                {
                  "_icon": "toolbar",
                  "text": "touchBar",
                  "cls": "Wb.Toolbar",
                  "properties": {
                    "cid": "touchBar",
                    "visible": "@Wb.isTouch",
                    "plainIcon": "true",
                    "alignSelf": "center"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "fileItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "fileItem",
                        "tip": "Send file",
                        "icon": "folder-open",
                        "handler": "app.sendFile"
                      },
                      "hasDupCid": 1
                    },
                    {
                      "_icon": "item",
                      "text": "sendItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "sendItem",
                        "type": "primary",
                        "text": "Send",
                        "padding": "0 .4em",
                        "handler": "app.sendText"
                      }
                    }
                  ]
                }
              ]
            },
            {
              "_icon": "button",
              "text": "sendBtn",
              "cls": "Wb.Button",
              "properties": {
                "cid": "sendBtn",
                "text": "Send",
                "alignSelf": "end",
                "margin": "true",
                "visible": "@Wb.isDesktop",
                "type": "primary",
                "cname": "simpleButton",
                "handler": "app.sendText"
              }
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "tools"
              },
              "text": "tools",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Item",
                  "properties": {
                    "cid": "chatInfoItem",
                    "icon": "ellipsis"
                  },
                  "text": "chatInfoItem",
                  "_expanded": true,
                  "_icon": "item"
                }
              ]
            }
          ]
        },
        {
          "_icon": "dialog",
          "text": "userDetailDialog",
          "cls": "Wb.Dialog",
          "properties": {
            "cid": "userDetailDialog",
            "autoScroll": "true"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "component",
              "text": "summaryComp",
              "cls": "Wb.Component",
              "properties": {
                "cid": "summaryComp",
                "tpl": "<div class=\"w-row w-gap1\">\n  <div class=\"w-icon icon-user2 w-size3 w-aligns-center\"></div>\n  <div class=\"w-column w-gap\">\n    <div class=\"w-row w-gap\">\n      <div class=\"w-title4\">{display_name}</div>\n      <div class=\"w-icon icon-user3 w-aligns-center\"></div>\n    </div>\n    <div>User name: {user_name}</div>\n    <div>User id: {sid}</div>\n    <div>E-mail: {email}</div>\n  </div>\n</div>",
                "padding": "1em"
              }
            },
            {
              "_icon": "line",
              "text": "line1",
              "cls": "Wb.Line",
              "properties": {
                "cid": "line1",
                "margin": "top"
              }
            },
            {
              "_icon": "options",
              "text": "sendMsgOption",
              "cls": "Wb.Option",
              "_expanded": false,
              "properties": {
                "cid": "sendMsgOption",
                "text": "Send message",
                "icon": "message",
                "buttonStyle": "true"
              },
              "events": {
                "click": "let data = app.openUserData;\napp.userDetailDialog.close(true); // optional\napp.openChat({ userid: data.sid, display_name: data.display_name });"
              }
            },
            {
              "_icon": "padding",
              "text": "gap1",
              "cls": "Wb.Gap",
              "properties": {
                "cid": "gap1"
              }
            },
            {
              "_icon": "options",
              "text": "noteOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "noteOption",
                "text": "Set notes and labels",
                "border": "bottom"
              },
              "events": {
                "click": "Wb.tip(this.text);"
              },
              "_expanded": true
            },
            {
              "_icon": "options",
              "text": "permOption",
              "cls": "Wb.Option",
              "properties": {
                "cid": "permOption",
                "text": "Friend permissions"
              },
              "events": {
                "click": "Wb.tip(this.text);"
              }
            },
            {
              "_icon": "padding",
              "text": "gap2",
              "cls": "Wb.Gap",
              "properties": {
                "cid": "gap2"
              }
            },
            {
              "_icon": "options",
              "text": "momentOption",
              "cls": "Wb.Option",
              "_expanded": true,
              "properties": {
                "cid": "momentOption",
                "height": "5em",
                "text": "Moments",
                "border": "bottom"
              },
              "events": {
                "click": "Wb.tip(this.text);"
              }
            },
            {
              "_icon": "options",
              "text": "moreOption",
              "cls": "Wb.Option",
              "_expanded": false,
              "properties": {
                "cid": "moreOption",
                "text": "More information"
              },
              "events": {
                "click": "Wb.tip(this.text);"
              }
            },
            {
              "_icon": "padding",
              "text": "gap3",
              "cls": "Wb.Gap",
              "_expanded": true,
              "properties": {
                "cid": "gap3",
                "flex": "1"
              }
            }
          ]
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "tab",
          "text": "tab1",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "tab1",
            "touchStyle": "true",
            "cls": "d-we-chat"
          },
          "events": {
            "cardchange": "if (newCard)\n  this.title = newCard.title;",
            "ready": "this.title = this.firstItem.title;"
          },
          "items": [
            {
              "_icon": "card",
              "text": "chatsCard",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "chatsCard",
                "icon": "message",
                "closable": "null",
                "title": "Chats",
                "layout": "fit"
              },
              "items": [
                {
                  "_icon": "list1",
                  "text": "chatsView",
                  "cls": "Wb.ListView",
                  "properties": {
                    "cid": "chatsView",
                    "datevalueField": "send_date",
                    "textField": "display_name",
                    "url": "@xpath + '/actions&xaction=getChats'",
                    "border": "false",
                    "subtextField": "text_content",
                    "defaultFields": "({\n  _icon: { value: 'user2' },\n  iconvalue: { value: 'bell-off' }\n})",
                    "cursorPointer": "true",
                    "selectColor": "active"
                  },
                  "events": {
                    "itemclick": "app.openChat(item.data);"
                  }
                }
              ]
            },
            {
              "_icon": "card",
              "text": "contactsCard",
              "cls": "Wb.Card",
              "properties": {
                "cid": "contactsCard",
                "icon": "address-book",
                "closable": "null",
                "title": "Contacts",
                "layout": "fit"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "list1",
                  "text": "contactsView",
                  "cls": "Wb.ListView",
                  "properties": {
                    "cid": "contactsView",
                    "textField": "display_name",
                    "url": "@xpath + '/actions&xaction=getContacts'",
                    "selectColor": "active",
                    "border": "false",
                    "defaultFields": "({\n  _icon: { value: 'user2' }\n})",
                    "cursorPointer": "true"
                  },
                  "events": {
                    "itemclick": "app.openUser(item.data);"
                  }
                }
              ]
            },
            {
              "_icon": "card",
              "text": "discoverCard",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "discoverCard",
                "icon": "discovery",
                "closable": "null",
                "title": "Discover",
                "layout": "fit"
              },
              "items": [
                {
                  "_icon": "list-view",
                  "text": "discoverView",
                  "cls": "Wb.View",
                  "properties": {
                    "cid": "discoverView",
                    "data": "[\n  { icon: 'moment', title: 'Moment' },\n  { gap: true, _disabled: true },\n  { icon: 'video', title: 'Video' },\n  { icon: 'broadcast', title: 'Live' },\n  { gap: true, _disabled: true },\n  { icon: 'scan', title: 'Scan' },\n  { icon: 'command', title: 'Shake ' },\n  { gap: true, _disabled: true },\n  { icon: 'search-file', title: 'Search ' },\n  { gap: true, _disabled: true },\n  { icon: 'cart', title: 'Shopping ' },\n  { icon: 'game', title: 'Game ' },\n  { gap: true, _disabled: true },\n  { icon: 'data-provider', title: 'Programs ' }\n]",
                    "itemTpl": "{if data.gap}\n<div class=\"w-item w-fixed-bgcolor-x\" style=\"height:.6em\"></div>\n{else}\n<div class=\"w-item w-border w-row w-padding1 w-align-center w-gapd5 w-main-bgcolor w-pointer\">\n  <div class=\"w-icon icon-{icon} w-size1d5\"></div>\n  <div class=\"w-name\">{title}</div>\n  <div class=\"w-flex\"></div>\n  <div class=\"w-icon icon-right2 w-sub-color\"></div>\n</div>\n{/if}",
                    "background": "true",
                    "cls": "w-disp-grid",
                    "border": "false"
                  },
                  "events": {
                    "itemclick": "Wb.tip(item.data.title + ' clicked');"
                  }
                }
              ]
            },
            {
              "_icon": "card",
              "text": "serviceCard",
              "cls": "Wb.Card",
              "_expanded": true,
              "properties": {
                "cid": "serviceCard",
                "icon": "briefcase",
                "closable": "null",
                "title": "Service",
                "layout": "fit"
              },
              "events": {
                "click": "let item = e.target.getClosestComp(Wb.ViewItem);\nif (item)\n  Wb.tip(item.data.text + ' clicked');"
              },
              "items": [
                {
                  "_icon": "container",
                  "text": "serviceCt",
                  "cls": "Wb.Container",
                  "properties": {
                    "cid": "serviceCt",
                    "layout": "grid1",
                    "gap": ".5em",
                    "padding": ".5em",
                    "background": "true"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "_icon": "component",
                      "text": "bannerComp",
                      "cls": "Wb.Component",
                      "properties": {
                        "cid": "bannerComp",
                        "html": "<div class=\"w-row w-justify-around w-padding2\" style=\"background:#4caf50\">\n  <div class=\"w-column w-padding w-gap w-align-center w-pointer w-unselect\" xid=\"payment\">\n    <div class=\"w-icon icon-check9 w-size2\" style=\"color:#fff;\"></div>\n    <div style=\"color: #fff;\">Payment</div>\n  </div>\n  <div class=\"w-column w-padding w-gap w-align-center w-pointer w-unselect\" xid=\"wallet\">\n    <div class=\"w-icon icon-card w-size2\" style=\"color:#fff;\"></div>\n    <div style=\"color: #fff;\">Wallet</div>\n  </div>\n</div>"
                      },
                      "events": {
                        "click": "let el = e.target.closest('[xid]');\nif (el)\n  Wb.tip(el.getAttribute('xid') + ' clicked');"
                      }
                    },
                    {
                      "_icon": "panel",
                      "text": "financialPanel",
                      "cls": "Wb.Panel",
                      "properties": {
                        "cid": "financialPanel",
                        "title": "Financial Planning",
                        "icon": "money",
                        "layout": "grid1",
                        "roundBorder": "true",
                        "padding": "false",
                        "plainTitle": "true"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "_icon": "list-view",
                          "text": "financialView",
                          "cls": "Wb.View",
                          "properties": {
                            "cid": "financialView",
                            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5 w-pointer\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-label w-ta-center\">{text}</div>\n</div>",
                            "layout": "grid4",
                            "data": "[\n  { icon: 'card', text: 'Credit Card' },\n  { icon: 'money', text: 'Loan' },\n  { icon: 'command', text: 'Wealth' },\n  { icon: 'component', text: 'Insurance' }\n]",
                            "bodyCls": "w-grid4",
                            "padding": "2em",
                            "border": "false",
                            "clickSelect": "false",
                            "gap": "1em"
                          },
                          "_expanded": true
                        }
                      ]
                    },
                    {
                      "_icon": "panel",
                      "text": "lifePanel",
                      "cls": "Wb.Panel",
                      "properties": {
                        "cid": "lifePanel",
                        "title": "Life Services",
                        "icon": "handshake",
                        "layout": "grid1",
                        "roundBorder": "true",
                        "padding": "false",
                        "plainTitle": "true"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "_icon": "list-view",
                          "text": "lifeView",
                          "cls": "Wb.View",
                          "properties": {
                            "cid": "lifeView",
                            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5 w-pointer\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-label w-ta-center\">{text}</div>\n</div>",
                            "layout": "grid4",
                            "data": "[\n  { icon: 'mobile', text: 'Recharge' },\n  { icon: 'module', text: 'Pay' },\n  { icon: 'qq', text: 'QQ Coin' },\n  { icon: 'building', text: 'City Service' },\n  { icon: 'heart1', text: 'Public Welfare' },\n  { icon: 'disc', text: 'Health' }\n]",
                            "bodyCls": "w-grid4",
                            "padding": "2em",
                            "border": "false",
                            "clickSelect": "false",
                            "gap": "1em"
                          },
                          "_expanded": true
                        }
                      ]
                    },
                    {
                      "_icon": "panel",
                      "text": "transportationPanel",
                      "cls": "Wb.Panel",
                      "properties": {
                        "cid": "transportationPanel",
                        "plainTitle": "true",
                        "title": "Transportation ",
                        "icon": "car",
                        "layout": "grid1",
                        "roundBorder": "true",
                        "padding": "false"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "_icon": "list-view",
                          "text": "transportationView",
                          "cls": "Wb.View",
                          "properties": {
                            "cid": "transportationView",
                            "itemTpl": "<div class=\"w-item w-column w-align-center w-padding w-gapd5 w-pointer\">\n  <div class=\"w-size2 w-icon icon-{icon}\"></div>\n  <div class=\"w-label w-ta-center\">{text}</div>\n</div>",
                            "layout": "grid4",
                            "data": "[\n  { icon: 'location', text: 'Travel' },\n  { icon: 'train', text: 'Train Tickets' },\n  { icon: 'car', text: 'Taxi' },\n  { icon: 'school', text: 'Hotel' },\n  { icon: 'identity', text: 'Identity' }\n]",
                            "bodyCls": "w-grid4",
                            "padding": "2em",
                            "border": "false",
                            "clickSelect": "false",
                            "gap": "1em"
                          },
                          "_expanded": true
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "true",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Select leave data.\n   */\n  selectLeave() {\n    let result = Wb.getRow('select * from wb_leave where flow_id={?flowId?}');\n    result.flowTitle = new Wb.Workflow({ flowId: Params.flowId }).title;\n    Wb.send(result);\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: after-action.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.info('after flow action: ' + app.action); // action name\nWb.info('after flow id: ' + app.flow.flowId); // workflow id\n//return 'value'; // return to client"
  },
  "_icon": "module"
}

File name: before-action.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.info('before flow action: ' + app.action); // action name\nWb.info('before flow id: ' + app.flow.flowId); // workflow id\n//return 'value'; // return to client"
  },
  "_icon": "module"
}

File name: handle-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let flow, row;\n\n  flow = new Wb.Workflow({ flowId: Params.flowId });\n  Params.userid = flow.userid;\n  row = Wb.getRow('select * from wb_leave where flow_id={?flowId?} and user_id={?userid?}');\n  row.applicant = flow.getInitUser().displayName;\n  Params.leaveData = Wb.encode(row);\n}\nmain();"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "/**\n * Xwl comments.\n * @class $path\n */\nWb.apply(app, {\n  /** @property {Boolean} - Whether to hide default action buttons. */\n  hideActionButtons: true,\n  /** @property {Object} - Static leave data. @priv */\n  leaveData: _$leaveData$_,\n  /**\n   * Fires after displaying the window.\n   * @param {String} flowId The workflow instance id.\n   * @return {String} type Window type.\n   * -'start': Start window.\n   * -'handle': Handle window.\n   * -'view': View window.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  init(flowId, type, record) {\n    Wb.setValue(app.main, app.leaveData);\n    app.radioGroup1.visible = type == 'handle';\n    Wb.log(record.data.node_name);\n  },\n  /**\n   * Fires when the OK button is clicked.\n   */\n  onAction() {\n    let value;\n    switch (app.radioGroup1.value) {\n      /**\n       * Do pass action.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doPass\n       */\n      case 0:\n        app.doPass();\n        break;\n      /**\n       * Do back action.\n       * @param {String} nodeName The back to node name.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doBack\n       */\n      case 1:\n        value = app.nodeSelect.value;\n        if (value)\n          app.doBack(value);\n        else {\n          app.nodeSelect.focus();\n          Wb.tipSelect();\n        }\n        break;\n      /**\n       * Do transfer action.\n       * @param {String} user The user id.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doTransfer\n       */\n      case 2:\n        value = app.userSelect.value;\n        if (value)\n          app.doTransfer(value);\n        else {\n          app.userSelect.focus();\n          Wb.tipSelect();\n        }\n        break;\n      /**\n       * Do reject action.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doReject\n       */\n      case 3:\n        app.doReject();\n        break;\n      /**\n       * Do sign before action.\n       * @param {String} user The user id.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doSignBefore\n       */\n      case 4:\n        value = app.userSelect.value;\n        if (value)\n          app.doSignBefore(value);\n        else {\n          app.userSelect.focus();\n          Wb.tipSelect();\n        }\n        break;\n      /**\n       * Do sign after action.\n       * @param {String} user The user id.\n       * @param {Object} params The parameters object.\n       * @param {Function} callback The callback  function after executed. \n       * @function doSignAfter\n       */\n      case 5:\n        value = app.userSelect.value;\n        if (value)\n          app.doSignAfter(value);\n        else {\n          app.userSelect.focus();\n          Wb.tipSelect();\n        }\n        break;\n    }\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "column",
        "icon": "paper-plane",
        "title": "Leave application",
        "visible": "true",
        "padding": "true",
        "autoScroll": "true",
        "width": "80em"
      },
      "items": [
        {
          "_icon": "radios",
          "text": "radioGroup1",
          "cls": "Wb.RadioGroup",
          "properties": {
            "cid": "radioGroup1"
          },
          "events": {
            "change": "let value = this.value;\n\nif (value == 3)\n  value = 0;\nelse if (value == 4 || value == 5)\n  value = 2;\napp.cardCt1.activeIndex = value;\nif (value == 1 && !app.nodeSelect.value) {\n  app.nodeSelect.picker.load({ success() { this.parent.valueIndex = 0 } });\n}"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "check2",
              "text": "passRadio",
              "cls": "Wb.Radio",
              "properties": {
                "cid": "passRadio",
                "label": "@Str.pass",
                "value": "true"
              }
            },
            {
              "_icon": "check2",
              "text": "backRadio",
              "cls": "Wb.Radio",
              "properties": {
                "cid": "backRadio",
                "label": "@Str.back1"
              }
            },
            {
              "_icon": "check2",
              "text": "transferRadio",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "transferRadio",
                "label": "@Str.transfer"
              }
            },
            {
              "_icon": "check2",
              "text": "rejectRadio",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "rejectRadio",
                "label": "@Str.reject"
              }
            },
            {
              "_icon": "check2",
              "text": "signBeforeRadio",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "signBeforeRadio",
                "label": "@Str.signBefore"
              }
            },
            {
              "_icon": "check2",
              "text": "signAfterRadio",
              "cls": "Wb.Radio",
              "_expanded": true,
              "properties": {
                "cid": "signAfterRadio",
                "label": "@Str.signAfter"
              }
            }
          ]
        },
        {
          "_icon": "win-restore",
          "text": "cardCt1",
          "cls": "Wb.CardCt",
          "properties": {
            "cid": "cardCt1",
            "border": "false"
          },
          "_expanded": true,
          "items": [
            {
              "_icon": "container",
              "text": "container1",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container1",
                "layout": "grid1"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "text",
                  "text": "applicant",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "applicant",
                    "text": "Applicant",
                    "readonly": "true"
                  },
                  "_expanded": true
                },
                {
                  "_icon": "calendar",
                  "text": "begin_date",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "begin_date",
                    "text": "Begin Date",
                    "readonly": "true"
                  }
                },
                {
                  "_icon": "calendar",
                  "text": "end_date",
                  "cls": "Wb.Date",
                  "properties": {
                    "cid": "end_date",
                    "text": "End Date",
                    "readonly": "true"
                  }
                },
                {
                  "_icon": "number-edit",
                  "text": "leave_days",
                  "cls": "Wb.Number",
                  "properties": {
                    "cid": "leave_days",
                    "text": "Leave Days",
                    "readonly": "true"
                  }
                },
                {
                  "_icon": "text",
                  "text": "leave_reason",
                  "cls": "Wb.Text",
                  "properties": {
                    "cid": "leave_reason",
                    "text": "Leave Reason",
                    "readonly": "true"
                  },
                  "_expanded": true
                }
              ]
            },
            {
              "_icon": "container",
              "text": "container2",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container2",
                "layout": "grid1"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "combo",
                  "text": "nodeSelect",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "nodeSelect",
                    "url": "m?xwl=my/my-flow/get-his-nodes",
                    "text": "Select node",
                    "forceSelect": "true"
                  },
                  "events": {
                    "beforeload": "params.flowId = app.flowId;"
                  }
                }
              ]
            },
            {
              "_icon": "container",
              "text": "container3",
              "cls": "Wb.Container",
              "properties": {
                "cid": "container3",
                "layout": "grid1"
              },
              "_expanded": true,
              "items": [
                {
                  "_icon": "combo",
                  "text": "userSelect",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "userSelect",
                    "url": "m?xwl=sys/dialog/user-selector/select",
                    "textField": "display_name",
                    "valueField": "sid",
                    "subtextField": "user_name",
                    "text": "Select user",
                    "forceSelect": "true"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: start-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{container: false}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.apply(app, {\n  /**\n   * Fires after action execution.\n   * @param {String} action The action name.\n   * @param {Wb.Workflow} flow The workflow instance.\n   * @return {Object} The returned \"after\" parameter to the client.\n   */\n  afterAction(action, flow) {\n    if (flow.restarting) {\n      Params.$flow_id = flow.flowId;\n      Wb.sync({ tableName: 'wb_leave', update: Params, whereFields: 'flow_id' });\n    } else {\n      Wb.apply(Params, { sid: Wb.getId(), flow_id: flow.flowId, user_id: flow.handleUser });\n      Wb.sync({ tableName: 'wb_leave', insert: Params });\n    }\n  }\n});"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Boolean} - Whether to auto show window. */\n  autoShow: false,\n  /**\n   * Fires after displaying the window.\n   * @param {String} flowId The workflow instance id.\n   * @return {String} type Window type.\n   * -'start': Start window.\n   * -'handle': Handle window.\n   * -'view': View window.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  init(flowId, type, record) {\n    if (flowId) {\n      Wb.ajax({\n        url: xpath + '/../actions&xaction=selectLeave',\n        params: { flowId },\n        json: true,\n        success(resp) {\n          Wb.setValue(app.main, resp);\n          app.main.show();\n        }\n      });\n    } else {\n      app.main.show();\n    }\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "grid1",
        "title": "Leave application",
        "icon": "paper-plane",
        "visible": "true",
        "autoScroll": "true"
      },
      "items": [
        {
          "_icon": "text",
          "text": "flowTitle",
          "cls": "Wb.Text",
          "properties": {
            "cid": "flowTitle",
            "text": "Title",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "begin_date",
          "cls": "Wb.Date",
          "_expanded": true,
          "properties": {
            "cid": "begin_date",
            "text": "Begin Date",
            "required": "true"
          }
        },
        {
          "_icon": "calendar",
          "text": "end_date",
          "cls": "Wb.Date",
          "_expanded": true,
          "properties": {
            "cid": "end_date",
            "text": "End Date",
            "required": "true"
          }
        },
        {
          "_icon": "number-edit",
          "text": "leave_days",
          "cls": "Wb.Number",
          "properties": {
            "cid": "leave_days",
            "decimalCount": "0",
            "text": "Leave days",
            "required": "true",
            "minValue": "0",
            "maxLength": "5"
          }
        },
        {
          "_icon": "text",
          "text": "leave_reason",
          "cls": "Wb.Text",
          "properties": {
            "cid": "leave_reason",
            "text": "Reason",
            "required": "true"
          }
        }
      ]
    }
  ]
}

File name: start.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let flow, now = new Date();\n\n  flow = new Wb.Workflow({ file: 'example/leave.flw', title: 'My leave application' });\n  flow.start();\n  Wb.sync({\n    tableName: 'wb_leave', insert: {\n      sid: Wb.getId(), begin_date: now.addDay(2), end_date: now.addDay(5),\n      flow_id: flow.flowId, user_id: Wb.userid, leave_days: 8, leave_reason: 'Go on vacation'\n    }\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: manual-start.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "grid1"
      },
      "items": [
        {
          "_icon": "title",
          "text": "title1",
          "cls": "Wb.Title",
          "properties": {
            "cid": "title1",
            "title": "Manual start a new workflow"
          }
        },
        {
          "_icon": "button",
          "text": "button1",
          "cls": "Wb.Button",
          "properties": {
            "cid": "button1",
            "type": "primary",
            "text": "Start a new workflow"
          },
          "events": {
            "click": "Wb.ajax({\n  url: xpath + '/start',\n  success() {\n    Wb.tip('The workflow started, click to view it.', null,\n      f => Wb.openNormal({\n        url: 'my-flow', success(scope) {\n          scope.grid1.reload();\n        }\n      }));\n  }\n});"
          },
          "_expanded": true
        }
      ]
    }
  ]
}

File name: actions.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "true",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Select start/restart data.\n   */\n  selectStartData() {\n    let result;\n\n    result = Wb.getRowx('select * from wb_reimburse where flow_id={?flowId?}');\n    result.flowTitle = new Wb.Workflow({ flowId: Params.flowId }).title;\n    Wb.send(result);\n  },\n  /**\n   * Select handle data.\n   */\n  selectHandleData() {\n    let flow, list, history;\n\n    flow = new Wb.Workflow({ flowId: Params.flowId });\n    // Specify userid to limit only the initiate user data to be visible\n    Params.userid = flow.userid;\n    list = Wb.getRows(`select exp_date,item_name,amount from wb_reimburse where flow_id={?flowId?}\n      and user_id={?userid?} order by exp_date`);\n    history = Wb.getRows(`select b.process_time,c.display_name,b.opinions from wb_reimburse a,\n       wb_ra_opinions b, wb_user c where a.flow_id={?flowId?} and a.user_id={?userid?} \n       and a.sid=b.reimburse_id and b.approver_user_id=c.sid\n       order by b.process_time`);\n    Wb.send({ list, history, applicant: flow.getInitUser().text });\n  }\n};\nactions[Params.xaction]();"
  },
  "_icon": "module"
}

File name: handle-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.apply(app, {\n  /**\n   * Fires before action execution.\n   * @param {String} action The action name.\n   * @param {Wb.Workflow} flow The workflow instance.\n   * @return {Object} The returned \"before\" parameter to the client.\n   */\n  beforeAction(action, flow) {\n  },\n  /**\n   * Fires after action execution.\n   * @param {String} action The action name.\n   * @param {Wb.Workflow} flow The workflow instance.\n   * @return {Object} The returned \"after\" parameter to the client.\n   */\n  afterAction(action, flow) {\n    let node = flow.beforeNode, reimburse_id = Wb.getRecord('select sid from wb_reimburse where flow_id={?flowId?}')?.[0],\n      node_text = flow.getNodeLabel(node);\n\n    // Params._newUser: New user id of transfer, signBefore, signAfter \n    // Params._backNode: Back to node name.\n    Wb.sync({\n      tableName: 'wb_ra_opinions',\n      insert: {\n        sid: Wb.getId(), reimburse_id, process_time: new Date(), approver_user_id: flow.handleUser, node_text,\n        opinions: 'Action: ' + action + '\\nNode: ' + node_text + '\\n' + Params.opinions\n      }\n    });\n    if (action == 'pass' && flow.completed) {\n      return { type: 'done', msg: 'Congratulations! The process has been fully completed.' };\n    }\n  }\n});"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {Boolean} - Whether to auto show window. */\n  autoShow: false,\n  /**\n   * Fires after displaying the window.\n   * @param {String} flowId The workflow instance id.\n   * @return {String} type Window type.\n   * -'start': Start window.\n   * -'handle': Handle window.\n   * -'view': View window.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  init(flowId, type, record) {\n    app.opinionsTitle.visible = app.opinions.visible = type == 'handle';\n    app.getData(flowId);\n    Wb.log(record.data.node_name);\n  },\n  /**\n   * Fires before action execution.\n   * @param {String} action The action name.\n   * @return {Object} Parameter object to be submitted. If returns false, the execution is canceled.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  beforeAction(action, record) {\n    Wb.log('action: ' + action + ', node name: ' + record.data.node_name);\n  },\n  /**\n   * Fires after action execution.\n   * @param {String} action The action name.\n   * @param {Object} response The response object.\n   * @param {Object} [.result] The flow record data.\n   * @param {Object} [.before] The returned value of server beforeAction.\n   * @param {Object} [.after] The returned value of server afterAction.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  afterAction(action, response, record) {\n    let afterData = response.after;\n    Wb.log('action: ' + action + ', node name: ' + record.data.node_name);\n    if (afterData?.type == 'done')\n      Wb.tip(afterData.msg);\n  },\n  /**\n   * Get window data. @priv\n   * @param {String} flowId The workflow instance id.\n   */\n  getData(flowId) {\n    Wb.ajax({\n      url: xpath + '/../actions&xaction=selectHandleData',\n      params: { flowId },\n      json: true,\n      success(resp) {\n        let amount = 0, { list, history, applicant } = resp;\n\n        app.grid1.addData(list);\n        list.forEach(item => {\n          amount += item.amount;\n        })\n        Wb.setValue(app.main, { applicant, amount });\n        createHistory();\n        app.main.show();\n        // show opinions history\n        function createHistory() {\n          let comp = app.opinionsHistory, titleEl;\n          comp.cls = 'w-padding w-sized8';\n          history.forEach((item, i) => {\n            titleEl = comp.addEl('w-row w-gap w-sub-color');\n            if (i > 0)\n              titleEl.cls = 'w-margin-top';\n            titleEl.addEl().textContent = item.process_time.dateValue.dateTimeText;\n            titleEl.addEl().textContent = item.display_name;\n            comp.addEl().textContent = item.opinions;\n          });\n        }\n      }\n    });\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "column",
        "icon": "money",
        "title": "Reimburse Form",
        "gap": "true",
        "padding": "true",
        "autoScroll": "true",
        "visible": "true",
        "width": "60em",
        "height": "40em"
      },
      "items": [
        {
          "_icon": "label1",
          "text": "applicant",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "applicant",
            "text": "Applicant"
          }
        },
        {
          "_icon": "label1",
          "text": "amount",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "amount",
            "text": "Total amount"
          }
        },
        {
          "_icon": "title",
          "text": "detailsTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "detailsTitle",
            "title": "Details"
          }
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "pagingBar": "false",
            "pageSize": "-1",
            "flex": "1",
            "minHeight": "8em",
            "fields": "({ process_time: { type: 'date' } })"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "Date",
                    "fieldName": "exp_date"
                  },
                  "text": "dateCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "itemNameCol",
                    "text": "Item name",
                    "fieldName": "item_name",
                    "width": "-1"
                  },
                  "text": "itemNameCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "amountCol",
                    "text": "Amount",
                    "fieldName": "amount",
                    "width": "10em"
                  },
                  "text": "amountCol",
                  "_expanded": true,
                  "_icon": "column"
                }
              ]
            }
          ]
        },
        {
          "_icon": "title",
          "text": "opinionsHistoryTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "opinionsHistoryTitle",
            "title": "Approval opinions history"
          },
          "_expanded": true
        },
        {
          "_icon": "component",
          "text": "opinionsHistory",
          "cls": "Wb.Component",
          "properties": {
            "cid": "opinionsHistory",
            "autoScroll": "true",
            "maxHeight": "10em",
            "cls": "w-pre"
          },
          "_expanded": true
        },
        {
          "_icon": "title",
          "text": "opinionsTitle",
          "cls": "Wb.Title",
          "properties": {
            "cid": "opinionsTitle",
            "title": "Approval opinions"
          }
        },
        {
          "_icon": "textarea",
          "text": "opinions",
          "cls": "Wb.TextArea",
          "properties": {
            "cid": "opinions",
            "height": "6em",
            "required": "true"
          }
        }
      ]
    }
  ]
}

File name: start-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{container: false}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.apply(app, {\n  /**\n   * Fires before action execution.\n   * @param {String} action The action name.\n   * @param {Wb.Workflow} flow The workflow instance.\n   * @return {Object} The returned \"before\" parameter to the client.\n   */\n  beforeAction(action, flow) {\n  },\n  /**\n   * Fires after action execution.\n   * @param {String} action The action name.\n   * @param {Wb.Workflow} flow The workflow instance.\n   * @return {Object} The returned \"after\" parameter to the client.\n   */\n  afterAction(action, flow) {\n    let flow_id = flow.flowId, user_id = Wb.userid, data = Wb.getObject('data');\n\n    flow_id = flow.flowId;\n    data.insert?.forEach(item => {\n      Wb.apply(item, { sid: Wb.getId(), flow_id, user_id });\n    });\n    Wb.sync(Wb.apply({ tableName: 'wb_reimburse' }, data));\n  }\n});"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "// app.flowId: The workflow id\n// app.restarting: Whether the workflow is restarting\nWb.apply(app, {\n  /** @property {Boolean} - Whether to auto show window. */\n  autoShow: false,\n  /**\n   * Fires after displaying the window.\n   * @param {String} flowId The workflow instance id.\n   * @return {String} type Window type.\n   * -'start': Start window.\n   * -'handle': Handle window.\n   * -'view': View window.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  init(flowId, type, record) {\n    if (flowId) {\n      app.grid1.load({\n        params: { flowId }, success(a, b, resp) {\n          app.flowTitle.value = resp.flowTitle;\n          app.main.show();\n        }\n      });\n    } else {\n      app.main.show();\n    }\n  },\n  /**\n   * Fires before action execution.\n   * @param {String} action The action name.\n   * @param {Wb.GridItem} record The workflow record.\n   * @return {Object} Parameter object to be submitted. If returns false, the execution is canceled.\n   */\n  beforeAction(action, record) {\n    let grid = app.grid1;\n    if (!grid.hasChild) {\n      Wb.tipWarn('Please input at least one item');\n      grid.startEdit(grid.addData({}), 1);\n      return false;\n    }\n    return { data: app.grid1.modifiedData };\n  },\n  /**\n   * Fires after action execution.\n   * @param {String} action The action name.\n   * @param {Object} response The response object.\n   * @param {Object} [.result] The flow record data.\n   * @param {Object} [.before] The returned value of server beforeAction.\n   * @param {Object} [.after] The returned value of server afterAction.\n   * @param {Wb.GridItem} record The workflow record.\n   */\n  afterAction(action, response, record) {\n    Wb.log('action: ' + action + ', node name: ' + record.data.node_name);\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "column",
        "width": "60em",
        "title": "Start reimbursement workflow",
        "icon": "money",
        "gap": "true",
        "padding": "true",
        "autoScroll": "true",
        "visible": "true",
        "height": "30em"
      },
      "items": [
        {
          "_icon": "text",
          "text": "flowTitle",
          "cls": "Wb.Text",
          "properties": {
            "cid": "flowTitle",
            "text": "Title",
            "required": "true"
          }
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "grid1",
            "editable": "true",
            "pagingBar": "false",
            "flex": "1",
            "minHeight": "8em",
            "url": "@xpath + '/../actions&xaction=selectStartData'",
            "pageSize": "-1",
            "autoLoad": "false"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "tbar",
                "isProperty": "true"
              },
              "text": "tbar",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "addBtn",
                    "text": "@Str.add",
                    "icon": "add",
                    "keys": "Ctrl+E"
                  },
                  "events": {
                    "click": "app.grid1.addRecord();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "text": "@Str.del",
                    "icon": "delete",
                    "keys": "Ctrl+D"
                  },
                  "events": {
                    "click": "app.grid1.delRecords();"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "dateCol",
                    "text": "Date",
                    "fieldName": "exp_date"
                  },
                  "text": "dateCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Date",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "calendar",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "itemNameCol",
                    "text": "Item name",
                    "fieldName": "item_name",
                    "width": "-1"
                  },
                  "text": "itemNameCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "amountCol",
                    "text": "Amount",
                    "fieldName": "amount",
                    "width": "10em"
                  },
                  "text": "amountCol",
                  "_expanded": true,
                  "_icon": "column",
                  "items": [
                    {
                      "cls": "Wb.Number",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "decimalCount": "2",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "number-edit",
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: start-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{container: false}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "grid1",
        "title": "Starting with fork ",
        "icon": "flow1",
        "visible": "true",
        "autoScroll": "true"
      },
      "items": [
        {
          "_icon": "number-edit",
          "text": "startValue",
          "cls": "Wb.Number",
          "properties": {
            "cid": "startValue",
            "decimalCount": "0",
            "text": "Value",
            "required": "true",
            "minValue": "0",
            "maxLength": "5"
          }
        }
      ]
    }
  ]
}

File name: home.xwl


{
  "title": "@home",
  "icon": "home",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "// Get desktop data\nfunction loadPageData() {\n  let data = Wb.getResource(['desktop', 'desktop'], [null, 'system']), items, url;\n\n  data = data[0] ?? data[1];\n  items = data?.portlets || [];\n  items.forEach(item => {\n    try {\n      url = item.moduleUrl;\n      item.script = Wb.permitx(url) && Wb.execute(url);\n    } catch (e) {\n      item.script = e?.toString();\n    }\n  });\n  Wb.set({ items: Wb.encode(items) });\n}\nloadPageData();",
    "remark": "My home page"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Execute after load.\n   */\n  onLoad() {\n    let card = parentContainer;\n\n    app.restorePanels();\n    if (card?.app?.inHome) {\n      let tab = card.parent;\n      app.viewport1.homeTab = tab; // don't assign to app, prevent tab auto destroy\n      app.onCardChange.call(tab, card);\n      tab.on('cardchange', app.onCardChange)\n    }\n  },\n  /**\n   * Execute after tab card change. @priv\n   */\n  onCardChange(card) {\n    let tree = this.app.moduleTree, enabled = card == parentContainer;\n\n    tree.draggable = enabled ? {\n      acceptDrop() {\n        let data = this.source[0].data, tags = data.tags, panel;\n\n        if (tags) {\n          tags = Wb.parse(tags);\n          if (tags.frame || tags.newWin) {\n            Wb.tipWarn(Str.cannotOpenModule);\n            return;\n          }\n        }\n        panel = app.createPanel({\n          moduleUrl: 'm?xwl=' + data.path, icon: data._icon, img: data._img, tags,\n          strTitle: data.strTitle, title: data.text\n        });\n        panel.intoView();\n        panel.highlight();\n      }\n    } : false;\n    tree.setEvents(enabled, 'itemdrag', app.onItemDrag);\n  },\n  /**\n   * Module tree items drag. @priv\n   */\n  onItemDrag(data) {\n    let d = data.source[0].data;\n    data.allowDrop = d._leaf && d.path != 'my/home';\n    data.mode = 'append';\n  },\n  /**\n   * Create module panel. @priv\n   * @param {Object} data Module data.\n   * @return {Wb.Panel} The created panel.\n   */\n  createPanel(data) {\n    let panel, configs, moduleUrl, tags = data.tags, val, script;\n\n    moduleUrl = data.moduleUrl;\n    configs = {\n      cname: 'panel', layout: 'fit', plainTitle: true, roundBorder: true, closable: true, maximizable: true,\n      moduleUrl, tags\n    };\n    val = data.col || 6;\n    configs.gridColValue = val;\n    configs.gridColumn = 'span ' + val;\n    val = data.row || 4;\n    configs.gridRowValue = val;\n    configs.gridRow = 'span ' + val;\n    if (data.icon)\n      configs.icon = data.icon;\n    else if (data.img)\n      configs.img = data.img;\n    if (data.strTitle) {\n      configs.strTitle = data.strTitle;\n      configs.title = Str[data.strTitle];\n    } else {\n      configs.title = data.title;\n    }\n    panel = app.viewport1.add(configs);\n    configs = {\n      container: panel, updateTitle: false, success(scope) {\n        let vp = panel.firstItem, tools;\n\n        if (vp instanceof Wb.Panel) {\n          vp.border = false;\n          tools = vp.tools?.filter(tool => tool.icon != 'close') ?? [];\n          if (vp.title)\n            panel.title = vp.title;\n          if (vp.icon)\n            panel.icon = vp.icon;\n          else if (vp.img)\n            panel.img = vp.img;\n        } else {\n          tools = [];\n        }\n        tools.push({\n          icon: 'menu', handler() {\n            let comp = this.up(p => p instanceof Wb.Panel);;\n            app.menuSetComp = comp;\n            app.colSlider.value = comp.gridColValue ?? 1;\n            app.rowSlider.value = comp.gridRowValue ?? 1;\n            app.mainMenu.showBy(this);\n          }\n        });\n        panel.tools = tools;\n        vp?.titleBar$?.destroy();\n        if (scope?.noTitlebar) {\n          panel.titleBar.hide();\n          panel.mon({\n            mouseenter() {\n              this.titleBar.show();\n            },\n            mouseleave() {\n              this.titleBar.hide();\n            }\n          })\n        }\n      }\n    };\n    script = data.script;\n    if (script)\n      configs.script = script;\n    else\n      configs.url = moduleUrl;\n    Wb.open(Wb.apply(configs, tags));\n    return panel;\n  },\n  /**\n   * Restore saved panels. @priv\n   */\n  restorePanels() {\n    let items = _$items$_;\n\n    items.forEach(item => {\n      app.createPanel(item);\n    });\n  }\n});"
  },
  "items": [
    {
      "_icon": "menu2",
      "text": "mainMenu",
      "cls": "Wb.Menu",
      "properties": {
        "cid": "mainMenu",
        "layout": "grid1",
        "defaults": "({ labelWidth: '5em' })",
        "minWidth": "18em",
        "gap": "true",
        "padding": "1em"
      },
      "_expanded": true,
      "items": [
        {
          "_icon": "slidebar",
          "text": "colSlider",
          "cls": "Wb.Slider",
          "properties": {
            "cid": "colSlider",
            "text": "@Str.column",
            "maxValue": "12",
            "minValue": "1",
            "suffix": "true"
          },
          "events": {
            "change": "app.menuSetComp.gridColValue = value;\napp.menuSetComp.gridColumn = 'span ' + value;"
          }
        },
        {
          "_icon": "slidebar",
          "text": "rowSlider",
          "cls": "Wb.Slider",
          "properties": {
            "cid": "rowSlider",
            "text": "@Str.row",
            "minValue": "1",
            "maxValue": "12",
            "suffix": "true"
          },
          "events": {
            "change": "app.menuSetComp.gridRowValue = value;\napp.menuSetComp.gridRow = 'span ' + value;"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "grid12",
        "background": "true",
        "padding": ".5em",
        "gap": ".5em",
        "itemsSortable": "true",
        "droppable": "wb.home",
        "bodyCls": "w-home-body"
      },
      "events": {
        "destroy": "app.viewport1.homeTab?.un('cardchange', app.onCardChange);",
        "beforesort": "// only allow drag title bar\nif (!dragComp?.titleBar.el.contains(e.target))\n  return false;"
      }
    }
  ]
}

File name: back.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Approval workflow withdrawn",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('back');\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-his-nodes.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get history nodes list",
    "serverScript": "function main() {\n  let recs, nodeName, distinctRecs = [];\n\n  recs = Wb.getRecord('select node_name from wb_flow where sid={?flowId?}');\n  Params.nodeName = recs?.[0];\n  recs = Wb.getAllRecords(`select node_text,node_name from wb_flow_user where flow_id={?flowId?}\n      and user_type<2 and node_name<>{?nodeName?} order by accept_time desc`);\n  recs.forEach(rec => {\n    nodeName = rec[1];\n    if (distinctRecs.every(item => item[1] != nodeName))\n      distinctRecs.push(rec);\n  });\n  distinctRecs.lastItem[0] = Str.initiator;\n  Wb.send(distinctRecs);\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-start-form.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get starting form",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let flow = new Wb.Workflow({ file: Params.file });\n  Wb.send({ form: Util.getForm(flow, 'start') });\n}\nmain();"
  },
  "_icon": "module"
}

File name: pass.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Approval workflow passed",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('pass');\n}\nmain();"
  },
  "_icon": "module"
}

File name: reject.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Approval workflow rejected",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('reject');\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select my flow list",
    "serverScript": "function main() {\n  let sql, result, userid = Wb.userid;\n\n  sql = `select a.sid,a.user_id,a.start_time,a.title,a.status,a.node_name,a.node_text,a.tpl_file,d.display_name,\n        b.status as user_status,b.user_type,b.node_name as user_node_name from\n        wb_flow a,\n        wb_flow_user b,\n        (select flow_id,max(accept_time) as accept_time from wb_flow_user where user_id={?sys.userid?} group by flow_id) c,\n        wb_user d\n        where a.sid=b.flow_id and b.flow_id=c.flow_id and b.accept_time=c.accept_time and b.user_id={?sys.userid?} and a.user_id=d.sid`;\n  sql += getTypeSql(Params.type);\n  if (Params.search) {\n    Wb.setLike('search');\n    sql += ' and a.title like {?search?}';\n  };\n  sql += Wb.getOrderSql({ display_name: 'd.display_name', $: 'a' });\n  result = Wb.getRowx({ sql });\n  result.items.forEach(item => item.initiate = item.user_id == userid ? 1 : 0);\n  Wb.send(result);\n}\n/**\n * Get select type sql clause.\n * @param {String} type Select type.\n */\nfunction getTypeSql(type) {\n  switch (type) {\n    case 'initiated':\n      return ' and a.user_id={?sys.userid?}';\n    case 'pendHandle':\n      return ' and a.status=1 and b.status<2 and b.user_type<2 and a.node_name=b.node_name';\n    case 'handled':\n      return ' and b.status>1';\n    case 'pass':\n      return ' and b.status=2';\n    case 'reject':\n      return ' and b.status=3';\n    case 'back':\n      return ' and b.status=4';\n    case 'transfer':\n      return ' and b.status=5';\n    case 'signBefore':\n      return ' and b.status=6';\n    case 'signAfter':\n      return ' and b.status=7';\n    case 'backed':\n      return ' and b.status=8';\n    default:\n      return '';\n  }\n}\nmain();"
  },
  "_icon": "module"
}

File name: sign-after.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add signature after approval",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('signAfter');\n}\nmain();"
  },
  "_icon": "module"
}

File name: sign-before.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Add signature before approval",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('signBefore');\n}\nmain();"
  },
  "_icon": "module"
}

File name: start.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Start a new workflow",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('start');\n}\nmain();"
  },
  "_icon": "module"
}

File name: terminate.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Terminate workflow",
    "serverScript": "function main() {\n  let ids = Wb.getObject('ids'), update = [], $user_id = Wb.userid, $status = 1, status = 0;\n\n  ids.forEach($sid => {\n    update.push({ $sid, $user_id, $status, status });\n  });\n  // [whereFields: 'sid,user_id,status']: Restrict the update of record\n  Wb.sync({ tableName: 'wb_flow', whereFields: 'sid,user_id,status', update });\n}\nmain();"
  },
  "_icon": "module"
}

File name: transfer.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Approval workflow transfered",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Util.doAction('transfer');\n}\nmain();"
  },
  "_icon": "module"
}

File name: view-progress.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "View the progress of workflow",
    "serverScript": "function main() {\n  let flow = new Wb.Workflow({ flowId: Params.flowId });\n\n  Wb.send({\n    flow: flow.flow, activeNode: flow.activeNode.data.name,\n    users: Wb.getRowx(`select a.accept_time,a.process_time,b.display_name,a.node_name,a.node_text,a.status,a.user_type\n        from wb_flow_user a, wb_user b where a.flow_id={?flowId?} and a.user_id=b.sid order by a.accept_time`)\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: view.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "View the form of workflow",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  let now = new Date(), flow = new Wb.Workflow({ flowId: Params.flowId }), form, update = [], userid = Wb.userid;\n\n  // Mark the given flow read\n  Wb.getObject('allFlowId').forEach(item => {\n    update.push({ $flow_id: item, $user_id: userid, $status: 0, status: 1, process_time: now });\n  });\n  Wb.sync({\n    tableName: 'wb_flow_user', whereFields: 'flow_id,user_id,status', unique: false, update\n  });\n  form = Wb.getBool('handle') ? Util.getForm(flow, 'handle') : Util.getForm(flow, 'view');\n  if (!form)\n    Wb.raise('The workflow is configured with neither handleForm nor viewForm.');\n  Wb.send({\n    form, back: flow.getProperty('back'), transfer: flow.getProperty('transfer'), reject: flow.getProperty('reject'),\n    signBefore: flow.getProperty('signBefore'), signAfter: flow.getProperty('signAfter'), restarting: flow.restarting\n  });\n}\nmain();"
  },
  "_icon": "module"
}

File name: my-flow.xwl


{
  "title": "@myWorkflow",
  "icon": "flow",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "My initiated or participated workflows",
    "links": "[\n  \"wb/libs/x6.js\",\n  \"wb/js/flow-designer.js\"\n]"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Open file dialog and select a workflow file to start a new workflow instance. @priv\n   */\n  start() {\n    Wb.promptFile((a, win, file) => {\n      Wb.ajax({\n        url: xpath + '/get-start-form',\n        params: { file },\n        json: true,\n        success(resp) {\n          let url = resp.form;\n          if (!url) {\n            Wb.error(Str.noStartForm);\n            return;\n          }\n          win.close();\n          if (url.endsWith('.xwl'))\n            url = Wb.getModuleUrl(url);\n          Wb.run({\n            url,\n            success(scope) {\n              let main = scope.main;\n              scope.flowTplFile = file;\n              if (main) {\n                main.hide(); // for fire show event\n                main.closeAction = 'destroy';\n                main.modal = true;\n                main.onEsc = main.onCancel;\n                main.onEnter = f => app.doAction('start', scope, {});\n                main.buttonsBar.add([\n                  { text: Str.ok, type: 'primary', icon: 'ok', cid: 'ok', handler: main.onEnter },\n                  { text: Str.cancel, icon: 'delete', handler() { main.close() } }\n                ]);\n                if (scope.autoShow ?? true) {\n                  main.show();\n                  main.center();\n                }\n              }\n              scope.init?.(null, 'start', {});\n            }\n          });\n        }\n      });\n    }, '|wb/system/resource/flow', '.flw', Str.selectFile, false, 'm?xwl=sys/file/get-resource');\n  },\n  /**\n   * Check whether the given workflow can be handled.\n   * @param {Object} data Workflow instance data.\n   * @return {Boolean} Returns true means can be handled, otherwise not.\n   */\n  canHandle(data) {\n    return data.node_name == data.user_node_name && data.user_type <= 1 && data.user_status < 2 && data.status == 1;\n  },\n  /**\n   * View the form of current selected workflow. @priv\n   * @param {Boolean} handle Whether to handle the workflow.\n   */\n  view(handle) {\n    let rec = app.grid1.selection, data = rec?.data, recs = app.grid1.selections, flowId = data?.sid;\n\n    if (!flowId) {\n      Wb.tipSelect();\n      return;\n    }\n    if (handle && !app.canHandle(data)) {\n      Wb.tip(Str.notNeedHandle);\n      return;\n    }\n    Wb.ajax({\n      url: xpath + '/view',\n      params: { flowId, handle, allFlowId: app.grid1.selectionsData.pluck('sid') },\n      json: true,\n      success(resp) {\n        let url = resp.form;\n        recs.forEach(rec => rec.data.user_status == 0 && rec.set('user_status', 1));\n        if (url.endsWith('.xwl'))\n          url = Wb.getModuleUrl(url);\n        Wb.run({\n          url,\n          params: { flowId },\n          success(scope) {\n            let main = scope.main;\n            scope.flowId = flowId;\n            scope.restarting = resp.restarting;\n            if (main) {\n              main.hide();\n              main.closeAction = 'destroy';\n              main.modal = true;\n              main.onEsc = main.onCancel;\n              if (handle) {\n                let btns = [], isStart = data.user_type == 0;\n                scope.doPass = (params, callback) => app.doAction('pass', scope, rec, params, callback);\n                scope.doReject = (params, callback) => app.doAction('reject', scope, rec, params, callback);\n                scope.doBack = (node, params, callback) => app.doAction('back', scope, rec,\n                  Wb.apply({ _backNode: node }, params), callback);\n                scope.doTransfer = (user, params, callback) => app.doAction('transfer', scope, rec,\n                  Wb.apply({ _newUser: user }, params), callback);\n                scope.doSignBefore = (user, params, callback) => app.doAction('signBefore', scope, rec,\n                  Wb.apply({ _newUser: user }, params), callback);\n                scope.doSignAfter = (user, params, callback) => app.doAction('signAfter', scope, rec,\n                  Wb.apply({ _newUser: user }, params), callback);\n                if (scope.hideActionButtons) {\n                  main.onEnter = scope.onAction;\n                  btns.push({ text: Str.ok, icon: 'ok', type: 'primary', handler: main.onEnter });\n                } else {\n                  main.onEnter = f => scope.doPass();\n                  btns.push({ text: isStart ? Str.ok : Str.pass, icon: 'ok', type: 'primary', handler: main.onEnter });\n                  if (!isStart && resp.reject == 'true') {\n                    btns.push({\n                      text: Str.reject, icon: 'delete4', handler() {\n                        if (app.onBeforeAction('reject', scope, rec) === false)\n                          return;\n                        Wb.confirm(Str.doConfirm.format(Str.reject), f => scope.doReject());\n                      }\n                    });\n                  }\n                  if (!isStart && resp.back != 'false')\n                    btns.push({\n                      text: Str.back1, icon: 'right4', handler() {\n                        if (app.onBeforeAction('back', scope, rec) === false)\n                          return;\n                        Wb.ajax({\n                          url: xpath + '/get-his-nodes',\n                          params: { flowId },\n                          json: true,\n                          success(resp) {\n                            let select = app.backNodeSelect, win = app.backWin;\n                            select.data = resp;\n                            select.valueIndex = 0;\n                            win.okHandler = f => {\n                              app.doAction('back', scope, rec, { _backNode: select.value }, f => win.close());\n                            };\n                            win.show();\n                          }\n                        });\n                      }\n                    });\n                  if (!isStart && resp.transfer != 'false') {\n                    btns.push({\n                      text: Str.transfer, icon: 'undo', handler() {\n                        app.doUserAction('transfer', scope, Str.transfer, rec);\n                      }\n                    });\n                  }\n                  if (!isStart && resp.signBefore != 'false') {\n                    btns.push({\n                      text: Str.signBefore, icon: 'column-before', handler() {\n                        app.doUserAction('signBefore', scope, Str.signBefore, rec);\n                      }\n                    });\n                  }\n                  if (!isStart && resp.signAfter != 'false') {\n                    btns.push({\n                      text: Str.signAfter, icon: 'column-after', handler() {\n                        app.doUserAction('signAfter', scope, Str.signAfter, rec);\n                      }\n                    });\n                  }\n                }\n                btns.push({ text: Str.cancel, icon: 'delete', handler() { main.close() } });\n                main.buttonsBar.add(btns);\n\n              } else {\n                main.buttonsBar.add({ text: Str.close, type: 'primary', handler() { main.close() } });\n              }\n              if (scope.autoShow ?? true) {\n                main.show();\n                main.center();\n              }\n            }\n            scope.init?.(flowId, handle ? 'handle' : 'view', rec);\n          }\n        });\n      }\n    });\n  },\n  /**\n   * Perform action related to user. @priv\n   * @param {String} action Action name.\n   * @param {Object} scope App scope.\n   * @param {String} title User dialog title.\n   * @param {Wb.GridItem} rec Current record.\n   */\n  doUserAction(action, scope, title, rec) {\n    if (app.onBeforeAction(action, scope, rec) !== false) {\n      Wb.run({\n        url: 'user-selector',\n        single: true,\n        success(userSel) {\n          userSel.getValue((data, win) => {\n            app.doAction(action, scope, rec, { _newUser: data.sid }, f => win.close());\n          }, null, title, true);\n        }\n      });\n    }\n  },\n  /**\n   * Fire before action execute. @priv\n   * @param {String} actionName The action name.\n   * @param {Object} scope Dialog window scope.\n   * @param {Wb.GridItem} record Current record.\n   * @return {Object/Boolean} Params, Boolean means whether to cancel execute.\n   */\n  onBeforeAction(actionName, scope, record) {\n    if (!Wb.verify(scope.main))\n      return false;\n    let params = scope.beforeAction?.(actionName, record);\n    if (params === false)\n      return false;\n    else\n      return params;\n  },\n  /**\n   * Do the specified action. @priv\n   * @param {String} actionName The action name.\n   * @param {Object} scope Dialog window scope.\n   * @param {Wb.ViewItem} rec Current selected record.\n   * @param {Object} [extraParams] Extra params to submit.\n   * @param {Function} [callback] Callback function after executed.\n   */\n  doAction(actionName, scope, rec, extraParams, callback) {\n    let params, win = scope.main, isStart = actionName == 'start';\n\n    params = app.onBeforeAction(actionName, scope, rec);\n    if (params === false)\n      return;\n    params = Wb.copy(params);\n    if (isStart)\n      params.flowTplFile = scope.flowTplFile;\n    else\n      params.flowId = scope.flowId;\n    if (extraParams)\n      Wb.apply(params, extraParams);\n    Wb.ajax({\n      url: xpath + '/' + actionName,\n      comps: win,\n      params,\n      json: true,\n      success(resp) {\n        let row = resp.result;\n        win.close();\n        if (isStart) {\n          rec = app.grid1.insertData(0, row);\n          rec.select();\n        } else {\n          if (row)\n            rec.update(row);\n          else\n            rec.destroy();\n        }\n        scope.afterAction?.(actionName, resp, rec);\n        callback?.();\n      }\n    });\n  },\n  /**\n   * Get processed node ids list from names. @priv\n   * @param {Array} cells Workflow graph cells.\n   * @param {Array} nodeNames Node names.\n   * @return {Array} Node id list.\n   */\n  getProcessedNodes(cells, nodeNames) {\n    let result = [];\n\n    cells.forEach(cell => {\n      if (nodeNames.includes(cell.data.name))\n        result.push(cell.id);\n    });\n    return result;\n  },\n  /**\n   * Get user status icon. @priv\n   * @param {Number} status User status.\n   * @param {Number} type User type.\n   * @return {String} Status icon.\n   */\n  getStatusIcon(status, type) {\n    let icon;\n    //0: unread, 1: read, 2: done, 3: reject, 4: back, 5: transfer, 6: sign before, 7: sign after, 8: backed\n    switch (status) {\n      case 0:\n        icon = type == 1 ? 'edit' : 'check1';\n        break;\n      case 1:\n      case 8:\n        icon = type == 1 ? 'check8' : 'check9';\n        break;\n      case 2:\n        icon = 'check9';\n        break;\n      case 3:\n        icon = 'delete4';\n        break;\n      case 4:\n        icon = 'right4';\n        break;\n      case 5:\n        icon = 'undo';\n        break;\n      case 6:\n        icon = 'column-before';\n        break;\n      case 7:\n        icon = 'column-after';\n        break;\n    }\n    return icon;\n  },\n  /**\n   * View the progress of current selected workflow. @priv\n   */\n  viewProgress() {\n    let data = app.grid1.selectionData, win = app.progressWin;\n\n    if (!data) {\n      Wb.tipSelect();\n      return;\n    }\n    Wb.ajax({\n      url: xpath + '/view-progress',\n      params: { flowId: data.sid },\n      json: true,\n      success(resp) {\n        let nodes = resp.nodes, activeNode = resp.activeNode, source, target, startId, cells, users = resp.users;\n\n        win.subTitle = data.title;\n        app.activeNode = activeNode;\n        app.userGrid.localData = users;\n        app.userGrid.load();\n        app.flowDesigner.flowData = resp.flow;\n        cells = app.flowDesigner.graph.getCells();\n        nodes = app.getProcessedNodes(cells, users.items.pluck('node_name'));\n        startId = cells.find(cell => cell.data.nodeType == 'start').id;\n        cells.forEach(cell => {\n          if (cell.isEdge()) {\n            source = cell.source.cell;\n            target = cell.target.cell;\n            if ((nodes.includes(source) || source == startId) && nodes.includes(target)) {\n              cell.setAttrs({ line: { stroke: '#68e' } });\n            }\n          } else {\n            if (cell.data.name == activeNode) {\n              cell.setAttrs({\n                body: { fill: '#ff5' },\n                label: { fill: '#000' }\n              })\n            } else if (nodes.includes(cell.id)) {\n              cell.setAttrs({\n                body: { fill: '#68e' },\n                label: { fill: '#fff' }\n              })\n            }\n          }\n        });\n        win.show();\n      }\n    });\n  },\n  /**\n   * Load grid with search controls.\n   */\n  load() {\n    app.grid1.delayLoad({ comps: app.viewport1.tbar });\n  }\n});"
  },
  "items": [
    {
      "_icon": "window",
      "text": "progressWin",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "progressWin",
        "layout": "column",
        "width": "80em",
        "height": "45em",
        "modal": "true",
        "icon": "flow1",
        "title": "@Str.progress",
        "closeOnEsc": "true"
      },
      "events": {
        "hide": "// free resource\napp.flowDesigner.graph.clearCells();\napp.userGrid.destroyAll();"
      },
      "items": [
        {
          "cls": "Wb.Array",
          "properties": {
            "cid": "buttons"
          },
          "text": "buttons",
          "_expanded": true,
          "_icon": "array",
          "items": [
            {
              "cls": "Wb.Button",
              "events": {
                "click": "app.progressWin.close();"
              },
              "properties": {
                "cid": "closeBtn",
                "text": "@Str.close",
                "type": "primary"
              },
              "text": "closeBtn",
              "_expanded": true,
              "_icon": "button"
            }
          ]
        },
        {
          "_icon": "file-json",
          "text": "graphScript",
          "cls": "Wb.Script",
          "properties": {
            "cid": "graphScript",
            "script": "({ cname: 'flowDesigner', viewMode: true, cid: 'flowDesigner', flex: 1 })"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "grid",
          "text": "userGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "userGrid",
            "height": "15em",
            "pagingBar": "false",
            "columnsSortable": "true",
            "sorters": "accept_time"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "acceptTimeCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "acceptTimeCol",
                    "fieldName": "accept_time",
                    "text": "@Str.startTime",
                    "render": "function main() {\n  let icon, status = data.status, userType = data.user_type;\n  icon = app.getStatusIcon(status, userType);\n  el.insertCellIcon(icon, value.dateTimeText);\n  el.parentNode.setCls(data.node_name == app.activeNode && userType <= 1 && status < 2, 'w-bold');\n  el.parentNode.setCls(status == 8, 'w-line-through');\n}\nmain();",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "usernameCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "usernameCol",
                    "fieldName": "display_name",
                    "text": "@Str.username",
                    "width": "15em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "nodeCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "nodeCol",
                    "fieldName": "node_text",
                    "text": "@Str.node",
                    "width": "15em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "statusCol",
                  "cls": "Wb.Column",
                  "_expanded": false,
                  "properties": {
                    "cid": "statusCol",
                    "fieldName": "status",
                    "text": "@Str.status",
                    "render": "function getText() {\n  const statusText = [Str.unread, Str.read, Str.pass, Str.reject, Str.back1, Str.transfer, Str.signBefore, Str.signAfter, Str.backed];\n  let text, status = data.status, userType = data.user_type, pendHandle = userType == 1 && status < 2,\n    pendRead = userType == 2 && status == 0;\n\n  if (data.node_name == app.activeNode && (pendHandle || pendRead)) {\n    text = pendHandle ? Str.pendHandle : Str.pendRead;\n  } else {\n    text = userType == 0 ? Str.initiator : statusText[value];\n  }\n  return text;\n}\nreturn getText();",
                    "align": "left",
                    "minWidth": "8em",
                    "width": "-1"
                  }
                },
                {
                  "_icon": "column",
                  "text": "processTimeCol",
                  "cls": "Wb.Column",
                  "_expanded": false,
                  "properties": {
                    "cid": "processTimeCol",
                    "fieldName": "process_time",
                    "text": "@Str.updateTime",
                    "width": "13em"
                  }
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "_icon": "window",
      "text": "backWin",
      "cls": "Wb.Window",
      "_expanded": false,
      "properties": {
        "cid": "backWin",
        "icon": "undo",
        "layout": "grid1",
        "dialog": "true",
        "title": "@Str.back1",
        "width": "45em"
      },
      "items": [
        {
          "_icon": "combo",
          "text": "backNodeSelect",
          "cls": "Wb.Select",
          "properties": {
            "cid": "backNodeSelect",
            "text": "@Str.node",
            "required": "true",
            "forceSelect": "true"
          }
        }
      ]
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "newItem",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "newItem",
                "text": "@Str.new",
                "icon": "add",
                "keys": "Ctrl+E"
              },
              "events": {
                "click": "app.start();"
              }
            },
            {
              "_icon": "item",
              "text": "viewItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "viewItem",
                "text": "@Str.view",
                "keys": "Ctrl+Q",
                "icon": "search"
              },
              "events": {
                "click": "app.view();"
              }
            },
            {
              "_icon": "item",
              "text": "handleItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "handleItem",
                "text": "@Str.handle",
                "keys": "Ctrl+J",
                "icon": "edit"
              },
              "events": {
                "click": "app.view(true);"
              }
            },
            {
              "_icon": "item",
              "text": "progressItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "progressItem",
                "text": "@Str.progress",
                "icon": "flow1",
                "keys": "Ctrl+U"
              },
              "events": {
                "click": "app.viewProgress();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "terminateItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "terminateItem",
                "text": "@Str.terminate",
                "icon": "delete",
                "keys": "Ctrl+D"
              },
              "events": {
                "click": "let sels = app.grid1.selections;\nif (!sels.length) {\n  Wb.tipSelect();\n  return;\n}\nif (sels.some(item => !item.data.initiate || item.data.status != 1)) {\n  Wb.tip(Str.terminateInitOnly);\n  return;\n}\nWb.confirm(Wb.getActionHint(sels, 'title', Str.terminate), f => {\n  Wb.ajax({\n    url: xpath + '/terminate',\n    params: { ids: sels.map(item => item.data.sid) },\n    success() {\n      sels.forEach(item => item.data.status == 1 && item.set('status', 0));\n    }\n  });\n});"
              }
            },
            {
              "_icon": "divider",
              "text": "divider2",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider2"
              }
            },
            {
              "_icon": "text",
              "text": "search",
              "cls": "Wb.Text",
              "properties": {
                "cid": "search",
                "clearButton": "true",
                "placeholder": "@Str.search",
                "flex": "1",
                "minWidth": "5em",
                "maxWidth": "20em"
              },
              "events": {
                "change": "app.load();"
              }
            },
            {
              "_icon": "divider",
              "text": "divider3",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider3"
              }
            },
            {
              "_icon": "combo",
              "text": "typeSelect",
              "cls": "Wb.Select",
              "properties": {
                "cid": "typeSelect",
                "editable": "false",
                "data": "[\n  { text: Str.all, value: 'all', _icon: 'flow1' },\n  { text: Str.myInitiated, value: 'initiated', _icon: 'paper-plane' },\n  { text: Str.pendHandle, value: 'pendHandle', _icon: 'check1' },\n  { text: Str.handled, value: 'handled', _icon: 'check9' },\n  { text: Str.pass, value: 'pass', _icon: 'ok' },\n  { text: Str.reject, value: 'reject', _icon: 'delete4' },\n  { text: Str.back, value: 'back', _icon: 'right4' },\n  { text: Str.transfer, value: 'transfer', _icon: 'undo' },\n  { text: Str.signBefore, value: 'signBefore', _icon: 'column-before' },\n  { text: Str.signAfter, value: 'signAfter', _icon: 'column-after' },\n  { text: Str.backed, value: 'backed', _icon: 'right4' }\n]",
                "valueIndex": "0"
              },
              "events": {
                "select": "app.load();"
              }
            }
          ]
        },
        {
          "_icon": "grid",
          "text": "grid1",
          "cls": "Wb.Grid",
          "_expanded": true,
          "properties": {
            "cid": "grid1",
            "sorters": "{name:'start_time', desc:true}",
            "url": "@xpath + '/select'",
            "columnsSortable": "true",
            "multiSelect": "true"
          },
          "events": {
            "itemdblclick": "app.view(app.canHandle(item.data));",
            "beforeload": "params.type = app.typeSelect?.value;"
          },
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "hasDupCid": 1,
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowCol",
                    "rowNum": "true"
                  },
                  "text": "rowCol",
                  "_expanded": true,
                  "_icon": "column",
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "startTimeCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "startTimeCol",
                    "text": "@Str.startTime",
                    "fieldName": "start_time",
                    "width": "14em",
                    "render": "function main() {\n  let status = data.status, userStatus = data.user_status, userType = data.user_type, icon;\n\n  switch (status) {\n    case 0:\n      icon = 'delete';\n      break;\n    case 1:\n      icon = app.getStatusIcon(userStatus, userType);\n      break;\n    case 2:\n      icon = 'ok';\n      break;\n    case 3:\n      icon = 'delete4';\n      break;\n  }\n  el.insertCellIcon(icon, value.dateTimeText);\n  el.parentNode.setCls(data.node_name == data.user_node_name && status == 1 && (userType <= 1 && userStatus < 2 ||\n    userType == 2 && userStatus == 0), 'w-bold');\n}\nmain();"
                  }
                },
                {
                  "_icon": "column",
                  "text": "initiatorCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "initiatorCol",
                    "text": "@Str.initiator",
                    "fieldName": "display_name",
                    "width": "10em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "titleCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "titleCol",
                    "text": "@Str.title",
                    "fieldName": "title",
                    "width": "-1"
                  }
                },
                {
                  "_icon": "column",
                  "text": "nodeCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nodeCol",
                    "text": "@Str.currentNode",
                    "fieldName": "node_text",
                    "width": "13em"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "column",
                  "text": "typeCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "typeCol",
                    "text": "@Str.type",
                    "fieldName": "tpl_file",
                    "width": "18em"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

File name: get-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get department data",
    "serverScript": "function main() {\n  let Util = Wb.load('../dialog-util.mjs');\n\n  Wb.send(Util.getData(Wb.getObject('ids'), 'select sid,dept_code,dept_name from wb_dept'));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select departments",
    "serverScript": "function main() {\n  let searchClause = '', query = Params.query, firstItem, sql, rows;\n\n  if (query) {\n    Wb.setLike('query');\n    searchClause = 'and (dept_code like {?query?} or dept_name like {?query?})'\n  } else {\n    Params.parent_id = Params.sid || '0';\n    searchClause = 'and a.parent_id={?parent_id?}';\n  }\n  sql = `\n        select a.sid, a.dept_code, a.dept_name, 'folder1' as \"_icon\",\n        case when (select count(*) from wb_dept b where b.parent_id=a.sid)>0 then 1 else 0 end as items\n        from wb_dept a where a.status=1 ${searchClause} order by a.dept_code\n        `;\n  rows = Wb.getRows({ sql, rs: query ? 100 : -1 });\n  if (!query && !Params.sid && (firstItem = rows[0]) && rows.length == 1)\n    firstItem._expanded = true;\n  Wb.send(rows);\n}\nmain();"
  },
  "_icon": "module"
}

File name: dept-selector.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Department selector",
    "links": "[\n  \"wb/modules/sys/dialog/dialog-util$.js\"\n]"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Show select window and get a single department.\n   * @param {Function} fn Callback function.\n   * @param {Object} .data The new selected department data.\n   * @param {Wb.Window} .win The edit window.\n   * @param {String} [id] The old department id.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValue(fn, id, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, id, subTitle, false, required);\n  },\n  /**\n   * Show select window and get multiple departments.\n   * @param {Function} fn Callback function.\n   * @param {Array} .data The new selected departments data list.\n   * @param {Wb.Window} .win The edit window.\n   * @param {Array} [ids] The old departments ids list.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValues(fn, ids, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, ids, subTitle, true, required);\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "@Str.selectDept",
        "icon": "dept",
        "width": "80em",
        "resetDialog": "true",
        "layout": "fit",
        "padding": "true",
        "height": "12em",
        "maxHeight": "calc(100vh - 5em)"
      },
      "items": [
        {
          "_icon": "combo",
          "text": "select1",
          "cls": "Wb.Select",
          "properties": {
            "cid": "select1",
            "url": "@xpath + '/select'",
            "textField": "dept_name",
            "valueField": "sid",
            "tagSortable": "true",
            "clearButton": "true",
            "tagWrap": "true",
            "treePicker": "true",
            "subtextField": "dept_code",
            "useTag": "true"
          }
        }
      ]
    }
  ]
}

File name: module-selector.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Module selector",
    "serverScript": "Params.modulePath = Wb.encode(Base.modulePathText);"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /** @property {String} - Module path. @priv */\n  modulePath: _$modulePath$_,\n  /**\n   * Show select window.\n   * @param {Function} fn Callback function.\n   * @param {String} .path The new selected module path.\n   * @param {Wb.Window} .win The edit window.\n   * @param {String} [path] The old module path.\n   * @param {String} [subTitle] Window sub title.\n   */\n  getValue(fn, path, subTitle) {\n    let win = app.window1, doGet, tree = app.tree1;\n\n    doGet = f => {\n      win.okHandler = f => {\n        let data = tree.selectionData;\n\n        if (data?._leaf)\n          fn?.(data.path.slice(app.modulePath.length), win);\n        else\n          Wb.tipSelect();\n      }\n      win.show();\n      tree.focus();\n    }\n    win.subTitle = subTitle;\n    if (path)\n      tree.selectPath(path, doGet);\n    else\n      doGet();\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "@Str.selectModule",
        "icon": "module",
        "width": "60em",
        "layout": "fit",
        "height": "30em",
        "dialog": "true"
      },
      "items": [
        {
          "_icon": "tree-view",
          "text": "tree1",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "tree1",
            "url": "m?xwl=sys/file/get&type=ide&moduleSort=true",
            "fields": "({\n  _icon: {\n    convert(v, data) {\n      return data.icon || (data._leaf ? 'module' : 'folder1')\n    }\n  },\n  title: {\n    convert(v) {\n      return v?.startsWith('@') ? Str[v.slice(1)] : v;\n    }\n  }\n})",
            "subtextField": "title"
          },
          "events": {
            "beforeload": "if (!configs.node)\n  params.path = app.modulePath;",
            "itemdblclick": "if (item.leaf)\n  app.window1.okHandler();"
          }
        },
        {
          "cls": "Wb.Array",
          "properties": {
            "cid": "tools"
          },
          "text": "tools",
          "_expanded": true,
          "_icon": "array",
          "items": [
            {
              "cls": "Wb.Item",
              "events": {
                "click": "app.tree1.loadSelect();"
              },
              "properties": {
                "cid": "refreshItem",
                "icon": "refresh",
                "tip": "@Str.refresh"
              },
              "text": "refreshItem",
              "_expanded": true,
              "_icon": "item"
            }
          ]
        }
      ]
    }
  ]
}

File name: get-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get role data",
    "serverScript": "function main() {\n  let Util = Wb.load('../dialog-util.mjs');\n\n  Wb.send(Util.getData(Wb.getObject('ids'), 'select sid,role_name from wb_role'));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select roles",
    "serverScript": "function main() {\n  let searchClause = '';\n\n  if (Params.query) {\n    Wb.setLike('query');\n    searchClause = 'and role_name like {?query?}'\n  }\n  Wb.sendRows(`select sid,role_name from wb_role where status=1 ${searchClause} order by role_name`);\n}\nmain();"
  },
  "_icon": "module"
}

File name: role-selector.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Role selector",
    "links": "[\n  \"wb/modules/sys/dialog/dialog-util$.js\"\n]"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Show select window and get a single role.\n   * @param {Function} fn Callback function.\n   * @param {Object} .data The new selected role data.\n   * @param {Wb.Window} .win The edit window.\n   * @param {String} [id] The old role id.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValue(fn, id, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, id, subTitle, false, required);\n  },\n  /**\n   * Show select window and get multiple roles.\n   * @param {Function} fn Callback function.\n   * @param {Array} .data The new selected roles data list.\n   * @param {Wb.Window} .win The edit window.\n   * @param {Array} [ids] The old roles ids list.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValues(fn, ids, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, ids, subTitle, true, required);\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "@Str.selectRole",
        "icon": "role",
        "width": "80em",
        "resetDialog": "true",
        "layout": "fit",
        "padding": "true",
        "height": "12em",
        "maxHeight": "calc(100vh - 5em)"
      },
      "items": [
        {
          "_icon": "combo",
          "text": "select1",
          "cls": "Wb.Select",
          "properties": {
            "cid": "select1",
            "url": "@xpath + '/select'",
            "textField": "role_name",
            "valueField": "sid",
            "tagSortable": "true",
            "clearButton": "true",
            "tagWrap": "true",
            "useTag": "true"
          }
        }
      ]
    }
  ]
}

File name: get-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get user data",
    "serverScript": "function main() {\n  let Util = Wb.load('../dialog-util.mjs');\n\n  Wb.send(Util.getData(Wb.getObject('ids'), 'select sid,user_name,display_name from wb_user'));\n}\nmain();"
  },
  "_icon": "module"
}

File name: select.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Select users",
    "serverScript": "function main() {\n  let searchClause = '';\n\n  if (Params.query) {\n    Wb.setLike('query');\n    searchClause = 'and (user_name like {?query?} or display_name like {?query?})'\n  }\n  Wb.sendRows(`select sid,user_name,display_name from wb_user where status=1 ${searchClause} order by user_name`);\n}\nmain();"
  },
  "_icon": "module"
}

File name: user-selector.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "User selector",
    "links": "[\n  \"wb/modules/sys/dialog/dialog-util$.js\"\n]"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.apply(app, {\n  /**\n   * Show select window and get a single user.\n   * @param {Function} fn Callback function.\n   * @param {Object} .data The new selected user data.\n   * @param {Wb.Window} .win The edit window.\n   * @param {String} [id] The old user id.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValue(fn, id, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, id, subTitle, false, required);\n  },\n  /**\n   * Show select window and get multiple users.\n   * @param {Function} fn Callback function.\n   * @param {Array} .data The new selected users data list.\n   * @param {Wb.Window} .win The edit window.\n   * @param {Array} [ids] The old users ids list.\n   * @param {String} [subTitle] Window sub title.\n   * @param {Boolean} [required] Whether is required.\n   */\n  getValues(fn, ids, subTitle, required) {\n    Wb.module.dialog.Util.getValue(app, xpath, fn, ids, subTitle, true, required);\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "window1",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "window1",
        "title": "@Str.selectUser",
        "icon": "users",
        "width": "80em",
        "resetDialog": "true",
        "layout": "fit",
        "padding": "true",
        "height": "12em",
        "maxHeight": "calc(100vh - 5em)"
      },
      "items": [
        {
          "_icon": "combo",
          "text": "select1",
          "cls": "Wb.Select",
          "properties": {
            "cid": "select1",
            "url": "@xpath + '/select'",
            "textField": "display_name",
            "valueField": "sid",
            "tagSortable": "true",
            "clearButton": "true",
            "subtextField": "user_name",
            "tagWrap": "true",
            "useTag": "true"
          }
        }
      ]
    }
  ]
}

File name: add.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let fs = Wb.load('./fs.mjs'), file, metaData, path, filename, fullname = Params.fullname;\n  metaData = Wb.applyValueWith({}, Params, ['title', 'icon', 'img', 'tags', 'hideInMenu']);\n  if (fullname) {\n    path = Wb.getDirectory(fullname);\n    filename = Wb.getFilename(fullname);\n  } else {\n    path = Params.path;\n    filename = Params.filename;\n  }\n  file = fs.addFile(path, filename, Wb.getBool('isFolder'), Params.url, Params.relName, metaData);\n  Wb.send({ lastModified: file.lastModifiedDate, path: file.path });\n}\nmain();",
    "remark": "Create file in any path"
  },
  "_icon": "module",
  "title": "",
  "_expanded": true
}

File name: browser.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\n/**\n * Initialize file module.\n */\nfunction init() {\n  let realPath = Base.realPath, props = System.getProperties(), pathList, getPath = FileUtil.getPath;\n\n  pathList = [];\n  pathList.pushIf(\n    realPath ? getPath(realPath) : null,\n    Base.pathText,\n    getPath(props.getProperty('java.home')),\n    getPath(props.getProperty('user.home')),\n    getPath(props.getProperty('user.dir')),\n    getPath(props.getProperty('java.io.tmpdir'))\n  );\n  pathList = pathList.unique();\n  pathList.forEach((item, i) => item?.endsWith('/') && (pathList[i] = item.slice(0, -1)));\n  Params.params = Wb.encode({\n    pathList,\n    appPath: Base.pathText,\n    osCharset: Base.osCharset?.toUpperCase(),\n    charset: System.getProperty('file.encoding')?.toUpperCase(),\n    fileCharset: Wb.getConfig('sys.file.charset')?.toUpperCase(),\n    filenameCharset: Wb.getConfig('sys.locale.filenameCharset')?.toUpperCase(),\n    sysFilenameCharset: SysUtil.filenameCharset?.name()?.toUpperCase(),\n  });\n}\ninit();",
    "remark": "File browser shell"
  },
  "_icon": "module",
  "events": {
    "initialize": "/**\n * Xwl comments.\n * @class $path\n */\nWb.define(app, {\n  /** @property {Object} - Server params. */\n  params: _$params$_,\n  /** @property {String} - The URL used to get the files path. */\n  getFileUrl: 'm?xwl=sys/file/get',\n  /** @property {Boolean} - File tree raw path. */\n  set$rawPath(value) {\n    app.rawPath$ = value;\n    value ??= '';\n    app.basePath = value.includes('|') ? value.replace('|', app.params.appPath) : value;\n    app.rootPath = Wb.getParentDirectory(app.basePath);\n  },\n  get$rawPath() {\n    return app.rawPath$;\n  },\n  /** @property {String} - The base path of the tree. */\n  basePath: '',\n  /** @property {String} - The parent of the base path. */\n  rootPath: '',\n  /** @property {Array} - Path back list. @priv */\n  backList: [],\n  /** @property {Array} - Path forward list. @priv */\n  forwardList: [],\n  /** @property {String} - Current node path. @priv */\n  get$currentPath() {\n    return app.selNode.getPath(null, '|');\n  },\n  /** @property {String} - Current file path. @priv */\n  get$filePath() {\n    return app.selNode.data.path;\n  },\n  /** @property {Wb.TreeItem} - Current select node. @priv */\n  get$selNode() {\n    return app.fileTree.selection;\n  },\n  /**\n   * Go to the specified folder path.\n   * @param {String} path The folder path.\n   * @param {Function} Callback function after operation completion.\n   */\n  goto(path, callback) {\n    app.fileTree.selectPath(app.toTreePath(path), callback, null, '|');\n  },\n  /**\n   * Convert file path to tree path. @priv\n   * @return {String} Tree path.\n   */\n  toTreePath(path) {\n    if (path.endsWith('/'))\n      path = path.slice(0, -1);\n    path = path.split('/');\n    if (!app.rootPath && !path[0].endsWith('/'))\n      path[0] += '/';\n    return path.join('|');\n  },\n  /**\n   * Select a node.\n   * @param {TreeItem} node The node to be selected.\n   */\n  selectNode(node) {\n    let path = node.data.path;\n    app.fileGrid.load({ params: { path: path } });\n    app.upItem.disabled = !node.parent?.isNode;\n    if (path.occur('/') > 1 && path.endsWith('/'))\n      path = path.slice(0, -1);\n    app.pathSelect.value = app.getRelPath(path);\n  },\n  /**\n   * Add a new file or folder to the current folder. @priv\n   * @param {String} filename File or folder name.\n   * @param {Boolean} [isFolder] Whether is folder to be added. Default is false.\n   */\n  addFile(isFolder) {\n    Wb.prompt({ title: isFolder ? Str.addFolder : Str.addFile, icon: isFolder ? 'folder1' : 'file' },\n      { text: Str.name, required: true, cid: 'filename' }, function (values, win) {\n        Wb.ajax({\n          url: 'm?xwl=sys/file/add',\n          json: true,\n          params: { path: app.filePath, filename: values.filename, isFolder },\n          success(resp) {\n            let newItem = Wb.apply({ text: values.filename, size: isFolder ? null : 0, _leaf: !isFolder }, resp);\n\n            if (!newItem._leaf)\n              newItem.path += '/';\n            app.fileGrid.addData(newItem).select();\n            if (isFolder && app.selNode?.loaded) {\n              newItem.items = [];\n              app.selNode.addData(newItem);\n            }\n            win.close();\n          }\n        });\n      });\n  },\n  /**\n   * Import files to the current folder.\n   * @param {Boolean} [unzip] Whether to unzip after import.\n   */\n  importFiles(unzip) {\n    let node = app.selNode, params = app.params, items, win;\n\n    items = [{\n      text: Str.file, cid: 'files', multiple: true, required: true, cname: 'fileInput',\n      browseMode: true, flex: 1, minHeight: '5em'\n    }];\n    items.push({\n      cid: 'charset', cname: 'select', text: Str.charset, value: params.sysFilenameCharset,\n      data: ['UTF-8', params.charset, params.osCharset, params.fileCharset, params.filenameCharset].filter(a => a).unique()\n    });\n    win = Wb.prompt({\n      title: (unzip ? Str.importUnzip : Str.import) + ' - ' + node.text, autoGrid: 'up', layout: 'form1', icon: 'import'\n    }, items, (values, win) => {\n      app.doImport(win, unzip, f => win.close(), values.charset);\n    }, 'wb.file.import');\n    win.down('charset').visible = unzip;\n  },\n  /**\n   * Download the selected files.\n   * @param {Boolean} [zip] Whether to zip before export.\n   */\n  exportFiles(zip) {\n    let items = app.fileGrid.selectionsData;\n    if (items.length)\n      Wb.download('m?xwl=sys/file/export-files', { files: items.pluck('path'), zip }, 'POST');\n    else\n      Wb.tipSelect();\n  },\n  /**\n   * Do import files. @priv\n   * @param {Wb.Component/Wb.Component[]} comps Import components.\n   * @param {Boolean} unzip Whether to unzip after import.\n   * @param {Function} callback The callback function.\n   * @param {Function} charset The file name charset.\n   */\n  doImport(comps, unzip, callback, charset) {\n    let node = app.selNode;\n\n    Wb.ajax({\n      url: 'm?xwl=sys/file/import-files&mode=list',\n      comps,\n      uploadProgress: true,\n      json: true,\n      params: { path: node.data.path, unzip, charset },\n      success(resp) {\n        let newNodes, fileGrid = app.fileGrid, newItems = [];\n\n        newNodes = resp.filter(item => !fileGrid.some(sub => sub.data.text == item.text));\n        newNodes.forEach(item => newItems.push(Wb.applyWithout({}, item, 'items')));\n        fileGrid.addData(newItems)\n        fileGrid.selection = fileGrid.filter(sub => resp.some(item => item.text == sub.data.text));\n        if (node.loaded) {\n          newNodes = resp.filter(item => !item._leaf && !node.some(sub => sub.text == item.text));\n          node.addData(newNodes);\n        }\n        callback?.();\n      }\n    });\n  },\n  /**\n   * Copy or cut the selected files.\n   * @param {Event} e Event object.\n   * @param {Boolean} [cut] Whether to cut.\n   */\n  copy(e, cut) {\n    if (!document.activeElement.isInput) {\n      let files = app.fileGrid.selectionsData;\n      if (files.length) {\n        app.clipboard = files.pluck('path');\n        app.isCut = cut;\n      } else\n        Wb.tipSelect();\n      e.stopEvent();\n    }\n  },\n  /**\n   * Get relative path base on the root path.\n   * @param {String} path Full path.\n   * @return {String} Relative path.\n   */\n  getRelPath(path) {\n    let rootPath = app.rootPath, len = rootPath.length;\n\n    if (len) {\n      if (rootPath.includes('|'))\n        len = len + app.params.appPath.length;\n      path = path.substr(len);\n      if (path.startsWith('/'))\n        path = path.substr(1);\n      return path;\n    } else {\n      return path;\n    }\n  },\n  /**\n   * File grid double click handler.  @priv\n   * @param {Object} data The item data.\n   */\n  onGridDblClick(data) {\n    let path = data.path, title = Wb.getFilename(path), tip = path;\n    Wb.open({\n      url: Wb.isImageFile(path) ? 'image-viewer' : 'text-editor', title, tip, params: { path }\n    });\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row"
      },
      "_expanded": true,
      "events": {
        "ready": "let el = this.el;\n\napp.fileGrid.contextMenu = Wb.createMenu(app.viewport1.tbar[0].items);\napp.fileInput = new Wb.FileInput({ cid: 'files', multiple: true, visible: false, renderEl: BodyEl });\nel.ondragover = Event.pdHandler;\nel.ondrop = e => {\n  let fileInput = app.fileInput;\n\n  e.stopEvent();\n  fileInput.clear();\n  fileInput.addFile(e.dataTransfer.files);\n  app.doImport(fileInput);\n};"
      },
      "items": [
        {
          "_icon": "array",
          "text": "tbar",
          "cls": "Wb.Array",
          "properties": {
            "cid": "tbar"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "bar1",
                "isProperty": "true"
              },
              "text": "bar1",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "addFileBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addFileBtn",
                    "icon": "file",
                    "text": "@Str.addFile",
                    "keys": "Ctrl+J",
                    "bindModule": "sys/file/add.xwl"
                  },
                  "events": {
                    "click": "app.addFile();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "addFolderBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "addFolderBtn",
                    "icon": "folder1",
                    "text": "@Str.addFolder",
                    "keys": "Ctrl+Shift+J",
                    "bindModule": "sys/file/add.xwl"
                  },
                  "events": {
                    "click": "app.addFile(true);"
                  }
                },
                {
                  "_icon": "item",
                  "text": "renameBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "renameBtn",
                    "icon": "edit",
                    "text": "@Str.rename",
                    "keys": "F2",
                    "stopEvent": "false",
                    "bindModule": "sys/file/set-property.xwl"
                  },
                  "events": {
                    "click": "if (document.activeElement.isInput)\n  return;\nlet fileGrid = app.fileGrid, row = fileGrid.selection, oldName, path, selNode = app.selNode;\n\nif (!row) {\n  Wb.tipSelect();\n  return;\n}\noldName = row.data.text;\nfileGrid.startEdit(row, 'text', (value, row) => {\n  if (row.text != value) {\n    path = row.data.path;\n    app.isRenaming = true;\n    Wb.ajax({\n      url: 'm?xwl=sys/file/set-property',\n      json: true,\n      params: { rename: true, path, filename: value },\n      callback(resp, xhr) {\n        if (xhr.ok) {\n          let update = item => {\n            item.set({\n              text: value, path: resp.path + (item.data._leaf ? '' : '/'),\n              lastModified: new Date(resp.lastModified).textValue\n            });\n          }\n          update(row);\n          if (!row.data._leaf) {\n            row = selNode.findItem('text', oldName);\n            if (row) {\n              let newPath, len;\n\n              len = row.data.path.length;\n              update(row);\n              newPath = row.data.path;\n              row.cascade(node => {\n                node.data.path = newPath + node.data.path.substr(len);\n              });\n            }\n          }\n        }\n        if (app.pendSelNode) {\n          app.selectNode(app.pendSelNode);\n          app.pendSelNode = null;\n        }\n        app.isRenaming = false;\n      }\n    });\n  }\n  return false;\n});\nWb.Editor.activeEditor.select(0, oldName.lastIndexOf('.'));"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider1"
                  },
                  "_expanded": true,
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "cutBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "cutBtn",
                    "icon": "cut",
                    "text": "@Str.cut",
                    "keys": "Ctrl+X",
                    "stopEvent": "false",
                    "bindModule": "sys/file/paste-files.xwl"
                  },
                  "events": {
                    "click": "app.copy(e, true);"
                  }
                },
                {
                  "_icon": "item",
                  "text": "copyBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "copyBtn",
                    "icon": "copy",
                    "text": "@Str.copy",
                    "keys": "Ctrl+C",
                    "stopEvent": "false",
                    "bindModule": "sys/file/paste-files.xwl"
                  },
                  "events": {
                    "click": "app.copy(e);"
                  }
                },
                {
                  "_icon": "item",
                  "text": "pasteBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "pasteBtn",
                    "icon": "paste",
                    "text": "@Str.paste",
                    "keys": "Ctrl+V",
                    "stopEvent": "false",
                    "bindModule": "sys/file/paste-files.xwl"
                  },
                  "events": {
                    "click": "function paste() {\n  let files = app.clipboard;\n  if (document.activeElement.isInput || !files?.length)\n    return;\n\n  let path = app.filePath, isCut = app.isCut, conflictFile, fromPath;\n\n  if (isCut) {\n    let firstFile = files[0];\n    fromPath = Wb.getParentDirectory(firstFile);\n    if (fromPath == path) {\n      //same parent\n      app.clipboard = null;\n      return;\n    }\n  }\n  // check paste conflict\n  conflictFile = files.find(file => file.endsWith('/') && path.startsWith(file));\n  if (conflictFile) {\n    Wb.warn(Str.cannotPaste.format(conflictFile));\n    return;\n  }\n  if (isCut)\n    app.clipboard = null;\n  Wb.ajax({\n    url: 'm?xwl=sys/file/paste-files&mode=list',\n    json: true,\n    params: { source: files, isCut, dest: path },\n    success(resp) {\n      let newNodes, fileGrid = app.fileGrid, newFiles = resp.files;\n\n      app.selNode.addData(newFiles.filter(item => !item._leaf));\n      newFiles.forEach(item => delete item.data);\n      newNodes = fileGrid.addData(newFiles);\n      fileGrid.selection = newNodes;\n      if (isCut) {\n        let node = app.fileTree.findPath(app.toTreePath(app.getRelPath(fromPath)), null, '|')\n        if (node) {\n          if (node.loaded) {\n            node.each(child => {\n              if (files.includes(child.data.path))\n                child.destroy();\n            }, true, true);\n          } else {\n            if (!resp.hasFolder)\n              node.removeExpander();\n          }\n        }\n      }\n    }\n  });\n}\npaste();"
                  }
                },
                {
                  "_icon": "item",
                  "text": "delBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "delBtn",
                    "icon": "delete",
                    "text": "@Str.del",
                    "keys": "Shift+Delete",
                    "bindModule": "sys/file/remove.xwl"
                  },
                  "events": {
                    "click": "let fileGrid = app.fileGrid, files = fileGrid.selectionsData;\nif (files.length) {\n  Wb.confirm(Wb.getActionHint(files, 'text'), f => {\n    files = files.pluck('path');\n    Wb.ajax({\n      url: 'm?xwl=sys/file/remove',\n      params: { files },\n      success() {\n        let selNode = app.selNode;\n\n        fileGrid.delRecords();\n        if (selNode.loaded) {\n          selNode.each(item => {\n            if (files.includes(item.data.path))\n              item.destroy();\n          }, true, true);\n        } else if (!fileGrid.some(item => !item.data._leaf)) {\n          selNode.removeExpander();\n        }\n      }\n    });\n  });\n} else {\n  Wb.tipSelect();\n}"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider2",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider2"
                  },
                  "_expanded": true,
                  "hasDupCid": 1
                },
                {
                  "_icon": "split",
                  "text": "exportBtn",
                  "cls": "Wb.SplitButton",
                  "properties": {
                    "cid": "exportBtn",
                    "icon": "export",
                    "text": "@Str.export",
                    "bindModule": "sys/file/export-files.xwl"
                  },
                  "events": {
                    "click": "app.exportFiles();"
                  },
                  "_expanded": false,
                  "items": [
                    {
                      "cls": "Wb.Menu",
                      "properties": {
                        "cid": "menu",
                        "isProperty": "true"
                      },
                      "text": "menu",
                      "_expanded": true,
                      "_icon": "menu2",
                      "hasDupCid": 0,
                      "items": [
                        {
                          "_icon": "item",
                          "text": "exportZip",
                          "cls": "Wb.Item",
                          "_expanded": false,
                          "properties": {
                            "cid": "exportZip",
                            "text": "@Str.exportZip"
                          },
                          "events": {
                            "click": "app.exportFiles(true);"
                          }
                        }
                      ]
                    }
                  ]
                },
                {
                  "_icon": "split",
                  "text": "importBtn",
                  "cls": "Wb.SplitButton",
                  "properties": {
                    "cid": "importBtn",
                    "icon": "import",
                    "text": "@Str.import",
                    "bindModule": "sys/file/import-files.xwl"
                  },
                  "events": {
                    "click": "app.importFiles();"
                  },
                  "_expanded": false,
                  "items": [
                    {
                      "cls": "Wb.Menu",
                      "properties": {
                        "cid": "menu",
                        "isProperty": "true"
                      },
                      "text": "menu",
                      "_expanded": true,
                      "_icon": "menu2",
                      "hasDupCid": 0,
                      "items": [
                        {
                          "_icon": "item",
                          "text": "importUnzip",
                          "cls": "Wb.Item",
                          "_expanded": true,
                          "properties": {
                            "cid": "importUnzip",
                            "text": "@Str.importUnzip"
                          },
                          "events": {
                            "click": "app.importFiles(true);"
                          }
                        }
                      ]
                    }
                  ]
                },
                {
                  "_icon": "divider",
                  "text": "divider3",
                  "cls": "Wb.Divider",
                  "_expanded": true,
                  "properties": {
                    "cid": "divider3"
                  }
                },
                {
                  "_icon": "item",
                  "text": "refreshBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "refreshBtn",
                    "icon": "refresh",
                    "text": "@Str.refresh"
                  },
                  "events": {
                    "click": "app.fileTree.loadSelect();"
                  }
                }
              ]
            },
            {
              "cls": "Wb.Toolbar",
              "properties": {
                "cid": "bar2",
                "isProperty": "true"
              },
              "text": "bar2",
              "_expanded": true,
              "_icon": "toolbar",
              "hasDupCid": 0,
              "items": [
                {
                  "_icon": "item",
                  "text": "backBtn",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "backBtn",
                    "icon": "left4",
                    "disabled": "true",
                    "text": "@Str.back"
                  },
                  "events": {
                    "click": "let backList = app.backList, path = backList.pop();\napp.forwardList.push(app.currentPath);\napp.stopHistory = true;\napp.fileTree.selectPath(path, null, null, '|');\napp.stopHistory = false;\napp.backBtn.disabled = !backList.length;\napp.forwardBtn.disabled = false;"
                  }
                },
                {
                  "_icon": "item",
                  "text": "forwardBtn",
                  "cls": "Wb.Item",
                  "_expanded": false,
                  "properties": {
                    "cid": "forwardBtn",
                    "icon": "right4",
                    "disabled": "true",
                    "text": "@Str.forward"
                  },
                  "events": {
                    "click": "let forwardList = app.forwardList, path = forwardList.pop();\napp.backList.push(app.currentPath);\napp.stopHistory = true;\napp.fileTree.selectPath(path, null, null, '|');\napp.stopHistory = false;\napp.forwardBtn.disabled = !forwardList.length;\napp.backBtn.disabled = false;"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider1",
                  "cls": "Wb.Divider",
                  "_expanded": false,
                  "properties": {
                    "cid": "divider1"
                  },
                  "hasDupCid": 1
                },
                {
                  "_icon": "item",
                  "text": "upItem",
                  "cls": "Wb.Item",
                  "_expanded": true,
                  "properties": {
                    "cid": "upItem",
                    "icon": "up4",
                    "disabled": "true",
                    "text": "@Str.up"
                  },
                  "events": {
                    "click": "app.fileTree.selection = app.selNode.parent;"
                  }
                },
                {
                  "_icon": "divider",
                  "text": "divider2",
                  "cls": "Wb.Divider",
                  "properties": {
                    "cid": "divider2"
                  },
                  "_expanded": true,
                  "hasDupCid": 1
                },
                {
                  "_icon": "combo",
                  "text": "pathSelect",
                  "cls": "Wb.Select",
                  "properties": {
                    "cid": "pathSelect",
                    "flex": "1",
                    "minWidth": "10em",
                    "data": "app.params.pathList",
                    "selectMode": "false"
                  },
                  "events": {
                    "select": "app.goto(data.text);",
                    "keydown": "if (e.isGoKey) {\n  app.pathSelect.triggers.fireEvent('click');\n  app.pathSelect.collapse();\n  e.stopEvent();\n}"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "cls": "Wb.Item",
                      "events": {
                        "click": "let select = this.parent, path = select.value, text, data = select.data;\ntext = path.replaceAll('\\\\', '/');\napp.goto(text, node => {\n  if (node) {\n    if (!data.some(item => item.text == text))\n      data.insert(0, { text });\n  } else {\n    Wb.tipWarn(Str.notFound.format(path));\n  }\n});"
                      },
                      "properties": {
                        "cid": "triggers",
                        "isProperty": "true",
                        "icon": "right4",
                        "tip": "@Str.goto"
                      },
                      "text": "triggers",
                      "_expanded": true,
                      "_icon": "item",
                      "hasDupCid": 0
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "fileTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "fileTree",
            "width": "18em",
            "url": "@app.getFileUrl+'&type=tree'",
            "autoSelect": "true",
            "keyWalking": "true",
            "autoLoad": "@!Wb.parseBool('_$dialog$_')"
          },
          "events": {
            "beforeselect": "let node = this.selection;\nif (node && !app.stopHistory) {\n  let path = node.getPath(null, '|'), backList = app.backList;\n  if (backList.lastItem != path)\n    backList.push(path);\n  app.backBtn.disabled = false;\n}",
            "selectionchange": "if (app.isRenaming) {\n  app.pendSelNode = app.selNode;\n  return false;\n}\napp.selectNode(app.selNode);",
            "beforeload": "let basePath = app.basePath;\nif (!configs.node && basePath) {\n  let rootPath = app.rootPath;\n  params.path = rootPath;\n  params.filterName = Wb.getFilename(app.basePath) || app.basePath;\n}"
          }
        },
        {
          "_icon": "splitter",
          "text": "splitter1",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "splitter1"
          }
        },
        {
          "_icon": "grid",
          "text": "fileGrid",
          "cls": "Wb.Grid",
          "properties": {
            "cid": "fileGrid",
            "flex": "1",
            "url": "@app.getFileUrl+'&type=file'",
            "autoLoad": "false",
            "columnsSortable": "true",
            "sorters": "text",
            "stateId": "wb.file",
            "fields": "({\n  lastModified: { type: 'date' }, _icon: {\n    convert(value, data) {\n      return data._leaf ? Wb.getFileIcon(data.text) : 'folder1';\n    }\n  }\n})",
            "showIcon": "true",
            "keyWalking": "true",
            "multiSelect": "true",
            "gridLine": "false",
            "stripeRows": "false",
            "rectSelect": "true",
            "pageSize": "-1"
          },
          "events": {
            "beforeload": "/**\n * Set sorter params.\n */\nfunction setSorterParams() {\n  let sorter = app.fileGrid.sorters, sortMap = { text: 'name', type: 'type', size: 'size', lastModified: 'date' };\n\n  if (Wb.isString(sorter))\n    sorter = { name: sorter, desc: false };\n  params.sort = sortMap[sorter.name];\n  params.desc = sorter.desc;\n  params.filterExt = app.extNames;\n}\nsetSorterParams();",
            "itemdblclick": "let data = item.data;\nif (data._leaf) {\n  app.onGridDblClick(data);\n} else {\n  app.selNode.selectChild(data.text);\n}",
            "selectionchange": "let sels = this.selections, hint, len = sels.length;\n\nif (len) {\n  hint = Str.nItems.format(len);\n  if (sels.some(item => item.data._leaf)) {\n    let total = 0;\n\n    sels.forEach(item => {\n      if (item.data._leaf)\n        total += item.data.size;\n    });\n    hint += ', ' + Str.size + Str.labelSeparator + total.fileSize + ' (' + total.intText + ')';\n  }\n}\nelse {\n  hint = '';\n}\napp.fileLabel.text = hint;"
          },
          "_expanded": true,
          "items": [
            {
              "cls": "Wb.Array",
              "properties": {
                "cid": "columns"
              },
              "text": "columns",
              "_expanded": true,
              "_icon": "array",
              "items": [
                {
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "rowNumCol",
                    "rowNum": "true"
                  },
                  "text": "rowNumCol",
                  "_expanded": true,
                  "_icon": "column"
                },
                {
                  "_icon": "column",
                  "text": "nameCol",
                  "cls": "Wb.Column",
                  "properties": {
                    "cid": "nameCol",
                    "text": "@Str.name",
                    "fieldName": "text",
                    "width": "18em"
                  },
                  "_expanded": true,
                  "items": [
                    {
                      "cls": "Wb.Text",
                      "properties": {
                        "cid": "editor",
                        "isProperty": "true",
                        "valueType": "filename",
                        "required": "true"
                      },
                      "text": "editor",
                      "_expanded": true,
                      "_icon": "text",
                      "hasDupCid": 0
                    }
                  ]
                },
                {
                  "_icon": "column",
                  "text": "dateCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "dateCol",
                    "fieldName": "lastModified",
                    "text": "@Str.modifiedDate",
                    "width": "12em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "typeCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "typeCol",
                    "fieldName": "type",
                    "text": "@Str.type",
                    "render": "return data._leaf ? Wb.getFileExt(data.text) : Str.folder;",
                    "width": "8em"
                  }
                },
                {
                  "_icon": "column",
                  "text": "sizeCol",
                  "cls": "Wb.Column",
                  "_expanded": true,
                  "properties": {
                    "cid": "sizeCol",
                    "fieldName": "size",
                    "text": "@Str.size",
                    "type": "fileSize",
                    "width": "8em"
                  }
                }
              ]
            },
            {
              "_icon": "pagenum",
              "text": "pagingBar",
              "cls": "Wb.PagingBar",
              "properties": {
                "cid": "pagingBar",
                "isProperty": "true",
                "totalOnly": "true"
              },
              "events": {
                "ready": "app.fileLabel = this.insert(2, ['->', { cname: 'label' }, '|'])[1];"
              },
              "hasDupCid": 0
            }
          ]
        }
      ]
    }
  ]
}

File name: export-files.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let files = Wb.getObject('files'), file = new Wb.File(files[0]), zip = Wb.getBool('zip'), len = files.length;\n\n  if (zip || len > 1 || file.isFolder) {\n    let filename;\n\n    if (len == 1)\n      filename = file.isFile ? file.normalName : file.name;\n    else\n      filename = file.parent.name || 'file';\n    files = files.map(file => new File(file));\n    Wb.setContentType(filename + '.zip');\n    ZipUtil.zip(files, response.getOutputStream());\n  } else {\n    Wb.exportData(file);\n  }\n}\nmain();",
    "remark": "Export files in any path"
  },
  "_icon": "module"
}

File name: find-shortcut.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let file, result, path = Params.path;\n  file = UrlBuffer.getBuffer().get('/' + path);\n  if (file)\n    result = Base.modulePathText + file;\n  else {\n    file = new Wb.File(Base.modulePath, path + '.xwl');\n    if (file.exists)\n      result = file.path;\n    else\n      result = '';\n  }\n  Wb.send(result);\n}\nmain();",
    "remark": "Find file path of the specified URL shortcut"
  },
  "_icon": "module"
}

File name: get-property.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs');\nWb.send(fs.getFileProperty(Params.path));",
    "remark": "Get file property in any path"
  },
  "_icon": "module"
}

File name: get-resource.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.File.checkContains('|wb/system/resource', Params.path);\nWb.run('./get');",
    "remark": "Get files in resource path"
  },
  "_icon": "module",
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false"
}

File name: get.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  let fs = Wb.load('./fs.mjs'), items, path = Params.path, filterName = Params.filterName, filterExt = Params.filterExt;\n\n  items = fs.listFiles(path, Params.type, Params.sort, Wb.getBool('desc'), Wb.getBool('moduleSort'));\n  if (filterName) {\n    let baseFolder = items.find(item => item.text == filterName);\n    if (baseFolder) {\n      baseFolder._expanded = true;\n      items = [baseFolder];\n    } else {\n      Wb.raise(Str.notFound.format(path + filterName));\n    }\n  }\n  if (filterExt) {\n    filterExt = filterExt.splitTrim();\n    items = items.filter(item => {\n      return filterExt.some(ext => item.text.endsWith(ext) || !item._leaf);\n    });\n  }\n  Wb.send(items);\n}\nmain();",
    "remark": "Get files in any path"
  },
  "_icon": "module",
  "title": ""
}

File name: image-viewer.xwl


{
  "title": "",
  "icon": "image",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.set({ path: Wb.encode(Params.path) });",
    "remark": "Image viewer shell"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {String} src The src of image to preview. */\n  get$src() {\n    return app.imageEl.src;\n  },\n  set$src(value) {\n    app.imageEl.src = value;\n  }\n});\nWb.define(app, {\n  /** @property {String} path The file path to edit.  @priv */\n  path: _$path$_,\n  /**\n   * Preview the image file. @priv\n   */\n  preview(callback) {\n    let path = app.path;\n    Wb.ajax({\n      url: 'm?xwl=sys/file/open',\n      params: { path },\n      success(resp) {\n        app.imageEl.src = 'data:image/' + Wb.getFileExt(path) + ';base64,' + resp;\n      }\n    });\n  },\n  /**\n   * Execute after on load. @priv\n   */\n  onLoad() {\n    app.preview();\n    if (!Globals.WbMainTab)\n      document.title = Wb.getFilename(app.path);\n  }\n});"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "border": "false",
        "autoScroll": "true",
        "bodyCls": "w-prev-ct"
      },
      "events": {
        "ready": "app.imageEl = this.bodyEl.addEl('w-margin-center', 'img');"
      },
      "_expanded": true
    }
  ]
}

File name: import-files.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.checkDemo();\nfunction main() {\n  let fs = Wb.load('./fs.mjs'), newFile, files = Wb.getParams('files'), charset,\n    path = new Wb.File(Params.path), unzip = Wb.getBool('unzip'), newNames, filename, filenames = [];\n\n  charset = Params.charset ? SysUtil.getCharset(Params.charset) : SysUtil.filenameCharset;\n  files.forEach(file => {\n    filename = file.name;\n    if (unzip && Wb.getFileExt(filename).toLowerCase() == 'zip') {\n      newNames = ZipUtil.unzip(file.stream, path.file, charset);\n      filenames = filenames.concat(newNames);\n      filenames.forEach(name => {\n        new Wb.File(path.file, name).cascadeSelf(file => file.clearBuffer());\n      })\n    } else {\n      filenames.push(filename);\n      newFile = new Wb.File(path, filename);\n      newFile.stream = file.stream;\n      newFile.clearBuffer();\n    }\n  });\n  files = fs.listFiles(path, Params.mode);\n  Wb.send(files.filter(file => filenames.includes(file.text)));\n}\nmain();",
    "remark": "Import files in any path"
  },
  "_icon": "module"
}

File name: open-resource.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "Wb.File.checkContains('|wb/system/resource', Params.path);\nWb.run('./open');",
    "remark": "Get file content in resource path"
  },
  "_icon": "module",
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false"
}

File name: open.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs');\nWb.send(fs.getFileData(Params.path, Params.charset));",
    "remark": "Get file content in any path"
  },
  "_icon": "module",
  "title": ""
}

File name: paste-files.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs');\nWb.send(fs.pasteFiles(Wb.getObject('source'), Params.dest, Params.relName, Wb.getBool('isCut'), Params.mode));",
    "remark": "Paste or move files in any path"
  },
  "_icon": "module"
}

File name: remove.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs');\nfs.removeFiles(Wb.getObject('files'));",
    "remark": "Remove file in any path"
  },
  "_icon": "module",
  "title": ""
}

File name: save.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs');\nWb.send(fs.saveFile(Wb.payload, Params.confirm == '1'));",
    "remark": "Save file content in any path"
  },
  "_icon": "module",
  "title": ""
}

File name: set-property.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let fs = Wb.load('./fs.mjs'), renameOnly = Wb.getBool('rename'), file;\n\nfile = fs.setFileProperty(Params.path, Params.filename, renameOnly ? false : Params.title,\n  Params.icon, Params.img, Params.tags, Params.url, Params.hideInMenu);\nWb.send({ lastModified: file.lastModified, path: file.path });",
    "remark": "Set file property in any path"
  },
  "_icon": "module"
}

File name: text-editor.xwl


{
  "title": "",
  "icon": "text1",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let path = Params.path;\nWb.set('args', Wb.encode({\n  osCharset: Base.osCharset?.toUpperCase(),\n  charset: System.getProperty('file.encoding')?.toUpperCase(),\n  fileCharset: Wb.getConfig('sys.file.charset')?.toUpperCase(),\n  language: Wb.getFileExt(path),\n  filenameCharset: Wb.getConfig('sys.locale.filenameCharset')?.toUpperCase()\n}));\nWb.set({ path: Wb.encode(path) });",
    "remark": "Text editor shell"
  },
  "_icon": "module",
  "events": {
    "initialize": "Wb.define(app, {\n  /** @property {Object} - Server args. @priv */\n  args: _$args$_,\n  /** @property {String} path The file path to edit.  @priv */\n  path: _$path$_,\n  /** @property {String} charset Charset of text encoding.  @priv */\n  charset: 'UTF-8',\n  /**\n   * Reopen the file. @priv\n   * @param {Function} [callback] The function to be executed after open.\n   */\n  reOpen(callback) {\n    Wb.ajax({\n      url: 'm?xwl=sys/file/open',\n      params: { path: app.path, charset: app.charset },\n      success(resp) {\n        let pos = resp.indexOf('|');\n        app.lastModified = resp.substr(0, pos);\n        app.editor.rawValue = resp.substr(pos + 1);\n        app.saveBtn.disabled = true;\n        callback?.();\n      }\n    });\n  },\n  /**\n   * Set text charset. @priv\n   */\n  setCharset() {\n    let args = app.args;\n    Wb.prompt({ icon: 'earth', title: Str.setCharset }, [\n      {\n        cid: 'charset', cname: 'select', text: Str.charset, required: true, value: app.charset,\n        data: ['UTF-8', args.charset, args.osCharset, args.fileCharset, args.filenameCharset].filter(a => a).unique()\n      }, { cname: 'displayField', showEmptyLabel: true, icon: 'warn', value: Str.overwriteTextHint }\n    ], (values, win) => {\n      app.charsetItem.text = app.charset = values.charset;\n      app.reOpen(f => win.close());\n    });\n  },\n  /**\n   * Execute after on load. @priv\n   */\n  onLoad() {\n    app.reOpen();\n    if (!Globals.WbMainTab)\n      document.title = Wb.getFilename(app.path);\n    app.editor.focus();\n  },\n  /**\n   * Save the current file. @priv\n   * @param {Boolean} [noConfirm] Whether no confirm when the file has been modified.\n   */\n  save(noConfirm) {\n    if (app.saveBtn.disabled)\n      return;\n    let data, content = app.editor.value;\n\n    data = app.path + '|' + app.charset + '|' + app.lastModified + '|' + content.length + '|' + content;\n    Wb.ajax({\n      url: 'm?xwl=sys/file/save', data,\n      params: {\n        confirm: noConfirm ? 0 : 1\n      },\n      success(resp) {\n        app.lastModified = Wb.decode(resp)[0];\n        app.saveBtn.disabled = true;\n      },\n      failure(resp) {\n        if (resp.errorCode == 'modified')\n          Wb.confirm(Str.modifiedConfirm.format(Wb.decode(resp.errorText)[0]), f => app.save(true));\n      }\n    });\n  }\n});",
    "beforeunload": "if (!app.saveBtn.disabled)\n  return false;"
  },
  "_expanded": true,
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "_expanded": true,
      "properties": {
        "cid": "viewport1",
        "layout": "fit"
      },
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "saveBtn",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "saveBtn",
                "icon": "save",
                "text": "@Str.save",
                "disabled": "true",
                "keys": "Ctrl+S"
              },
              "events": {
                "click": "app.save();"
              }
            }
          ]
        },
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "bbar",
            "isProperty": "true"
          },
          "text": "bbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "fill1"
              }
            },
            {
              "_icon": "label",
              "text": "cursorLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "cursorLabel"
              }
            },
            {
              "_icon": "divider",
              "text": "divider1",
              "cls": "Wb.Divider",
              "properties": {
                "cid": "divider1"
              }
            },
            {
              "_icon": "item",
              "text": "charsetItem",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "charsetItem",
                "text": "@app.charset"
              },
              "events": {
                "click": "app.setCharset();"
              }
            },
            {
              "_icon": "space",
              "text": "space1",
              "cls": "Wb.Space",
              "properties": {
                "cid": "space1"
              }
            }
          ]
        },
        {
          "_icon": "edit",
          "text": "editor",
          "cls": "Wb.CodeEditor",
          "properties": {
            "cid": "editor",
            "wrapBorder": "false",
            "border": "true",
            "language": "@app.args.language"
          },
          "events": {
            "cursorchange": "app.cursorLabel.text = cursor ? (cursor.lineNumber + ' : ' + cursor.column) : Wb.nbsp;",
            "change": "app.saveBtn.disabled = false;"
          }
        }
      ]
    }
  ]
}

File name: get-langs.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get language list",
    "serverScript": "function main() {\n  let file = new Wb.File(true, 'wb/js/locale'), result = [];\n\n  file.listFiles().forEach(file => {\n    let name = file.name;\n    if (name.startsWith('wb-') && name.endsWith('.js') && !name.endsWith('.min.js'))\n      result.push(name.slice(3, -3));\n  })\n  Wb.send(result);\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-modules-tree.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get modules tree",
    "serverScript": "function main() {\n  const Util = Wb.load('./util.mjs');\n  Wb.send(Util.getModules());\n}\nmain();"
  },
  "_icon": "module"
}

File name: get-user-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get current user data",
    "serverScript": "function main() {\n  const protectedText = Wb.UserUtil.protectedText;\n  let sql;\n\n  sql = `select '${protectedText}' as password, '${protectedText}' as confirm_password, display_name, create_date,\n    login_times, email, mobile_phone, use_lang from wb_user where sid={?sys.userid?}`;\n  Wb.sendDict(sql, 'wb,');\n}\nmain();"
  },
  "_icon": "module"
}

File name: save-desktop.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Save desktop",
    "serverScript": "function main() {\n  let { value, asDefault } = Params;\n\n  if (!value.portlets) {\n    let data = Wb.getResource(['desktop', 'desktop'], [null, 'system']);\n\n    data = data[0] ?? data[1];\n    if (data?.portlets)\n      value.portlets = data.portlets;\n  }\n  if (asDefault)\n    Wb.permitx('set-resource-all') && Wb.run('set-resource-all');\n  else\n    Wb.permitx('set-resource') && Wb.run('set-resource');\n}\nmain();"
  },
  "_icon": "module"
}

File name: search-modules.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Search module names",
    "serverScript": "function main() {\n  let query = Params.query, fs = Wb.load('wb/modules/sys/file/fs.mjs');\n\n  //[] indicates a complete match\n  if (query.startsWith('[') && query.endsWith(']'))\n    query = query.slice(1, -1);\n  else if (!query.includes('*') && !query.includes('?'))\n    query = '*' + query + '*';\n  Wb.send(search(fs, Wb.File.moduleFolder.path, query).mixSort('text'));\n}\n/**\n * Search the module files in the entire module directory.\n * @param {Object} fs FS util object.\n * @param {String} path Search path.\n * @param {String} query Search keyname.\n * @return {Array} Search result.\n */\nfunction search(fs, path, query) {\n  let itemPath, items = fs.listFiles(path, 'home'), result = [];\n  items.forEach(item => {\n    itemPath = item.path;\n    if (item._leaf) {\n      if (item.text.wildcardMatch(query) || item.path.lastItem('/').wildcardMatch(query)) {\n        result.push(Wb.applyWith({}, item, ['text', '_icon', '_img', 'path', 'strTitle']));\n      }\n    } else {\n      result.pushAll(search(fs, itemPath, query));\n    }\n  });\n  return result;\n}\nmain();"
  },
  "_icon": "module"
}

File name: set-user-data.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Set current user data",
    "serverScript": "function main() {\n  const protectedText = Wb.UserUtil.protectedText;\n  let session, pwd = Params.password, fields = 'display_name,email,mobile_phone,use_lang';\n\n  if (pwd != protectedText)\n    fields += ',password';\n  Wb.set({\n    $sid: Wb.userid, password: Wb.UserUtil.encryptPassword(pwd)\n  });\n  Wb.sync({ tableName: 'wb_user', update: Params, fields });\n  session = request.getSession(false);\n  session?.setAttribute('sys.dispname', Params.display_name);\n  session?.setAttribute('sys.lang', Params.use_lang);\n}\nmain();"
  },
  "_icon": "module"
}

File name: home.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "{url: 'home', newWin: true}",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('./home/util.mjs');\n\n  if (!Util.checkLogin())\n    return;\n  const theme = Wb.get('sys.theme') || Wb.getConfig('sys.theme');\n  Wb.set(Util.getDesktopData());\n  Wb.set({\n    theme: Wb.encode(theme),\n    licensee: Wb.encode(Util.getLicensee()),\n    themeItems: Wb.encode(Util.getThemeItems(theme)),\n    title: Wb.getConfig('sys.app.title'),\n    username: Wb.encode(Wb.username),\n    dispname: Wb.encode(Wb.dispname),\n    setDefaultDesktop: Wb.permit('set-resource-all'),\n    heartbeat: Wb.getConfig('sys.session.heartbeatInterval'),\n    isDemo: Config.isDemo,\n    isShowPwdDialog: Util.isShowPwdDialog()\n  });\n}\nmain();",
    "title": "_$title$_",
    "remark": "Default home page",
    "loginRequired": "false"
  },
  "_icon": "module",
  "_expanded": true,
  "events": {
    "initialize": "/**\n * Default home page\n * @class $path\n */\nWb.define(app, {\n  /** @property {Boolean} - Whether is demo version. */\n  isDemo: _$isDemo$_,\n  /** @property {String} - Licensee name. */\n  licensee: _$licensee$_,\n  /** @property {Boolean} - Whether to show password dialog. */\n  isShowPwdDialog: _$isShowPwdDialog$_,\n  /** @property {String} - Current user name. */\n  username: _$username$_,\n  /** @property {String} - Current user display name. */\n  dispname: _$dispname$_,\n  /** @property {Boolean} - Home module flag. */\n  inHome: true,\n  /** @property {String} - Current theme @priv */\n  theme: _$theme$_,\n  /** @property {Object} - Gets the currently open module app. Returns null if no modules are open. */\n  get$activeModule() {\n    return app.moduleTab.activeCard?.module ?? null;\n  },\n  /** @property {String} - Account display name. */\n  get$accountName() {\n    return Str.myAccount + ' (' + this.dispname + ')';\n  },\n  /** @property {Object} - Gets the currently open module card. */\n  get$activeCard() {\n    return app.moduleTab.activeCard;\n  },\n  /**\n   * Link current module to the relative menu. @priv\n   * @param {Wb.Card} [card] The card to link with. Default is the current active card. \n   */\n  linkToMenu(card) {\n    let url = (card ?? app.activeCard)?.moduleUrl;\n\n    if (url?.startsWith('m?xwl=')) {\n      let p = url.indexOf('&'), firstPath, btn, pos;\n      url = url.substring(6, p == -1 ? url.length : p) + '.xwl';\n      if (app.topNavBar) {\n        pos = url.indexOf('/');\n        firstPath = url.substr(0, pos);\n        btn = app.topNavBar.find(btn => btn.bindPath === firstPath);\n        if (btn) {\n          btn.active = true;\n          app.selectTopButton(btn, f => {\n            app.moduleTree.selectPath(url.substr(pos + 1), node => {\n              if (node) {\n                node.select(null, null, true);\n                node.intoViewCenter();\n              }\n            }, 'filename', null, true, true);\n          });\n        }\n      } else {\n        app.moduleTree.selectPath(url, node => {\n          if (node) {\n            node.select(null, null, true);\n            node.intoViewCenter();\n          }\n        }, 'filename', null, true, true);\n      }\n    }\n  },\n  /**\n   * Opens the specified module.\n   * @param {Object} data Parameter object.\n   */\n  openModule(data) {\n    let tags = data.tags, configs;\n\n    configs = {\n      icon: data._icon, img: data._img, title: data.text, url: 'm?xwl=' + data.path, allowRefresh: true,\n      tags: { strTitle: data.strTitle }\n    };\n    if (tags) {\n      tags = Wb.parse(tags);\n      configs.tags.tags = tags;\n      Wb.apply(configs, tags);\n    }\n    if (configs.frame)\n      Wb.browse(configs);\n    else if (configs.newWin)\n      Wb.openWin(configs);\n    else\n      Wb.open(configs);\n  },\n  /**\n   * Show my Account dialog.\n   */\n  showMyAccount() {\n    Wb.ajax({\n      url: xpath + '/get-user-data',\n      json: true,\n      success(resp) {\n        let win = app.accountWin;\n\n        if (!win) {\n          win = app.accountWin = Wb.Dict.createColWindow(resp.columns, 'display_name,password,confirm_password,' +\n            'email,mobile_phone,use_lang', { title: Str.myAccount, icon: 'user2', width: '50em' });\n        }\n        Wb.setValue(win, resp.items[0]);\n        win.okHandler = (win, values) => {\n          if (values.password != values.confirm_password) {\n            Wb.warn(Str.pwdNotSame, f => win.down('password').focus(false, true));\n            return;\n          }\n          Wb.ajax({\n            url: xpath + '/set-user-data',\n            comps: win,\n            success() {\n              app.dispname = values.display_name;\n              app.accountItem.text = app.accountName;\n              win.hide();\n            }\n          });\n        };\n        win.subTitle = app.dispname;\n        win.show();\n      }\n    });\n  },\n  /**\n   * Fired after the page is ready.\n   */\n  onReady() {\n    let activeCard = app.moduleTab.activeCard;\n    app.moduleTab.setNavigate(app.backItem, app.forwardItem);\n    if (activeCard) {\n      //This event must be triggered again during initialization because home is not ready\n      app.moduleTab.fireEvent('cardchange', activeCard);\n    }\n    console.info('%cThis software is only authorized to%c' + (app.licensee || '(unlicensed)'),\n      'padding: 2px 8px; border-radius: 3px 0 0 3px; color: #fff; background: #606060;',\n      'padding: 2px 8px; border-radius: 0 3px 3px 0; color: #fff; background: #1475b2;');\n  },\n  /**\n   * Window resize event handler. @priv\n   */\n  onResize() {\n    let visible = app.treeItem.visible = Globals.smallScreen;\n    app.toggleTools(visible);\n  },\n  /**\n   * Open the specified module. @priv\n   * @param {Object} [data] Module data.\n   */\n  doOpenModule(data) {\n    let sp = app.treeSplitter;\n\n    app.openModule(data);\n    // floating module tree\n    if (sp.slideTarget)\n      sp.restoreTargetComp();\n    else if (app.treeItem.visible)\n      app.moduleTree.hide();\n  },\n  /**\n   * Toggle tool buttons to the toolbar or menu.\n   * @param {Boolean} [inMenu] Whether tools in the menu.\n   */\n  toggleTools(inMenu) {\n    if (inMenu != app.toolsInMenu) {\n      let btn = app.backItem, items = [], menuItem = app.menuItem, menu = menuItem.menu, configs;\n\n      app.toolsInMenu = inMenu;\n      while (btn) {\n        btn.visible = !inMenu;\n        if (inMenu) {\n          configs = {\n            text: btn.tip, icon: btn.icon, refToolBtn: btn, handler() {\n              this.refToolBtn.fireEvent('click');\n            }\n          };\n          if (btn.active != null)\n            configs.checked = btn.active;\n          items.push(configs);\n        }\n        btn = btn.nextSibling;\n        if (btn == menuItem)\n          break;\n      }\n      if (inMenu) {\n        items.push({ cname: 'divider', refToolBtn: 1 });\n        menu.insert(0, items);\n      } else {\n        menu.each(item => { item.refToolBtn && item.destroy() }, true, true);\n      }\n    }\n  },\n  /**\n   * Show password dialog for changes all default password.\n   */\n  showPwdDialog() {\n    Wb.run({ url: xpath + '/../reset-passwords' });\n  },\n  /**\n   * Create top navigate bar.\n   */\n  createTopNavBar() {\n    let fillItem = app.topFillItem, moduleTree = app.moduleTree, bar, data;\n\n    fillItem.hide();\n    app.lastNavButton = null;\n    bar = app.topNavBar = fillItem.parent.insertBefore({\n      cname: 'toolbar', cls: 'w-top-nav', alignSelf: 'stretch', style: 'padding:0', flex: 1, justify: 'center',\n      margin: '0 .5em', events: {\n        buttonclick(button) {\n          app.selectTopButton(button);\n        }\n      }\n    }, fillItem);\n    moduleTree.each(node => {\n      data = node.data;\n      bar.add({\n        icon: data._icon, img: data._img, cls: 'w-nav-btn', text: data.text, bindPath: data.path,\n        bindData: data._leaf ? data : null, groupName: 'nav'\n      });\n    });\n    moduleTree.data = null;\n    bar.firstItem?.fireEvent('click');\n  },\n  /**\n   * Select top menu bar button. @priv\n   * @param {Wb.Button} button Selected button.\n   * @param {Function} callback Execute after sucess.\n   */\n  selectTopButton(button, callback) {\n    let lastBtn = app.lastNavButton, moduleTree = app.moduleTree;\n    if (lastBtn) {\n      lastBtn.bindItems = moduleTree.data;\n      lastBtn.bindScrollTop = moduleTree.scrollEl.scrollTop;\n      lastBtn.bindSelPath = moduleTree.selection?.data.path;\n    }\n    if (button.bindData) {\n      lastBtn.active = true;\n      app.doOpenModule(button.bindData);\n      callback?.();\n    } else {\n      if (button.bindItems) {\n        moduleTree.data = button.bindItems;\n        if (button.bindSelPath)\n          moduleTree.down(node => node.data.path == button.bindSelPath)?.selectNoFocus();\n        moduleTree.scrollEl.scrollTop = button.bindScrollTop;\n        callback?.();\n      } else {\n        moduleTree.load({\n          params: { path: button.bindPath }, success() {\n            callback?.();\n          }\n        });\n      }\n      app.lastNavButton = button;\n    }\n  },\n  /**\n   * Destroy navigate bar and restore tree navigate.\n   */\n  restoreTreeNav() {\n    app.topNavBar?.destroy();\n    app.topNavBar = null;\n    app.topFillItem.show();\n    app.moduleTree.load();\n  },\n  /**\n   * Save desktop data.\n   * @param {Boolean} [asDefault] Whether to save as the default desktop.\n   */\n  saveDesktop(asDefault) {\n    function getPageData() {\n      let items = [], url, tree = app.moduleTree, expandNodes, strTitle;\n      app.moduleTab.each(card => {\n        url = card.moduleUrl;\n        if (url && card.allowRefresh) {\n          strTitle = card.strTitle;\n          items.push({\n            icon: card.icon ?? undefined, img: card.img ?? undefined, visible: card.visible, tags: card.tags,\n            title: strTitle ? undefined : card.title, strTitle, moduleUrl: card.moduleUrl\n          });\n        }\n      });\n      if (!app.noSaveExandNodes) {\n        expandNodes = [];\n        app.moduleTree.each(node => {\n          if (node.expanded)\n            expandNodes.push(node.data.strTitle || node.text);\n        });\n      }\n      return {\n        items, portlets: getPortlets(), treeVisible: tree.visible, treeWidth: tree.getStyle('width'),\n        expandNodes, showNavBar: !!app.topNavBar\n      };\n    }\n\n    function getPortlets() {\n      let card = app.moduleTab.find(card => card.moduleUrl == 'm?xwl=my/home'), items, strTitle;\n\n      if (card && !card.prepend) {\n        items = [];\n        card.down('viewport1')?.each(panel => {\n          strTitle = panel.strTitle;\n          items.push({\n            icon: panel.icon ?? undefined, img: panel.img ?? undefined, tags: panel.tags, col: panel.gridColValue,\n            row: panel.gridRowValue, title: strTitle ? undefined : panel.title, strTitle, moduleUrl: panel.moduleUrl\n          });\n        });\n      }\n      return items;\n    }\n    Wb.ajax({\n      url: xpath + '/save-desktop',\n      data: { name: 'desktop', value: getPageData(), userid: asDefault ? 'system' : undefined, asDefault },\n      success() {\n        Wb.tipDone(Str.saveDesktop);\n      }\n    });\n  }\n});",
    "finalize": "window.onresize = app.onResize;\napp.onResize();\nlet smallScreen = app.treeItem.visible;\nif (smallScreen)\n  app.moduleTree.visible = !app.moduleTab.firstItem;\nif (app.isShowPwdDialog)\n  app.showPwdDialog();\nif (!smallScreen && _$showNavBar$_)\n  app.createTopNavBar();"
  },
  "items": [
    {
      "_icon": "plug",
      "text": "socket",
      "cls": "Wb.Socket",
      "properties": {
        "cid": "socket",
        "name": "sys.home",
        "heartbeatInterval": "_$heartbeat$_",
        "autoReconnect": "true"
      },
      "events": {
        "message": "function main() {\n  let data = e.data, type, msg, method;\n\n  data = Wb.decode(data);\n  type = data.type;\n  switch (type) {\n    case 'info':\n    case 'warn':\n    case 'error':\n      msg = data.msg;\n      method = 'tip' + type.capital;\n      if (Wb.isObject(msg)) {\n        Wb[method](msg.msg ?? Str.newMessage, null, msg.url ? f => {\n          if (msg.newWin)\n            Wb.openWin(msg);\n          else\n            Wb.open(msg);\n        } : undefined);\n      } else {\n        Wb[method](msg);\n      }\n      break;\n  }\n}\nmain();",
        "failure": "if (!Wb.unloading) {\n  Wb.login((username, dispname) => {\n    app.dispname = dispname;\n    app.accountItem.text = app.accountName;\n    if (app.username != username) {\n      app.username = username;\n      location.reload();\n    }\n  });\n}"
      }
    },
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "row",
        "border": "false",
        "cls": "@app.theme=='dark'?'w-home':'w-home w-dark-home'"
      },
      "_expanded": true,
      "events": {
        "ready": "app.onReady();"
      },
      "items": [
        {
          "_icon": "toolbar",
          "text": "tbar",
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "gap": ".2em",
            "style": "padding:0 .5em;"
          },
          "_expanded": false,
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "item",
              "text": "treeItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "treeItem",
                "icon": "tree-view",
                "plainIcon": "true",
                "minWidth": "2em"
              },
              "events": {
                "click": "app.moduleTree.visible = !app.moduleTree.visible;"
              }
            },
            {
              "_icon": "logo",
              "text": "logoIcon",
              "cls": "Wb.Icon",
              "properties": {
                "cid": "logoIcon",
                "icon": "geejing",
                "fontSize": "1.5em"
              }
            },
            {
              "_icon": "label",
              "text": "titleLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "titleLabel",
                "text": "_$title$_"
              },
              "_expanded": true
            },
            {
              "_icon": "right1",
              "text": "topFillItem",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "topFillItem"
              }
            },
            {
              "_icon": "item",
              "text": "backItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "backItem",
                "tip": "@Str.back",
                "icon": "left4",
                "plainIcon": "true",
                "cls": "w-home-tbtn"
              }
            },
            {
              "_icon": "item",
              "text": "forwardItem",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "forwardItem",
                "tip": "@Str.forward",
                "icon": "right4",
                "plainIcon": "true",
                "cls": "w-home-tbtn"
              }
            },
            {
              "_icon": "item",
              "text": "refreshItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "refreshItem",
                "tip": "@Str.refresh",
                "icon": "refresh",
                "plainIcon": "true",
                "disabled": "true",
                "cls": "w-home-tbtn"
              },
              "events": {
                "click": "let card = app.moduleTab.activeCard;\nif (card) {\n  let configs = { url: card.moduleUrl, card, tabMode: 'reload' }, tags = card.tags;\n\n  if (tags) {\n    Wb.apply(configs, tags);\n    configs.tags = { tags };\n  }\n  Wb.open(configs);\n}"
              }
            },
            {
              "_icon": "item",
              "text": "homeItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "homeItem",
                "tip": "@Str.home",
                "icon": "home",
                "plainIcon": "true",
                "cls": "w-home-tbtn"
              },
              "events": {
                "click": "Wb.openNormal('m?xwl=my/home');"
              }
            },
            {
              "_icon": "item",
              "text": "linkItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "linkItem",
                "icon": "link",
                "active": "false",
                "plainIcon": "true",
                "tip": "@Str.moduleLinkToMenu",
                "cls": "w-home-tbtn"
              },
              "events": {
                "toggle": "if (active)\n  app.linkToMenu();"
              }
            },
            {
              "_icon": "item",
              "text": "menuItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "menuItem",
                "icon": "menu",
                "plainIcon": "true",
                "cls": "w-home-tbtn"
              },
              "items": [
                {
                  "cls": "Wb.Menu",
                  "properties": {
                    "cid": "menu",
                    "isProperty": "true",
                    "minWidth": "20em"
                  },
                  "text": "menu",
                  "_expanded": true,
                  "_icon": "menu2",
                  "events": {
                    "show": "let menu = this;\nmenu.fontSize = Wb.configs.fontSize; //avoid menu font changes\nmenu.each(item => { item.refToolBtn && (item.disabled = item.refToolBtn.disabled) });\napp.topNavItem.checked = !!app.topNavBar?.visible;",
                    "hide": "//Adapts the menu font to the current font\nthis.fontSize = null;"
                  },
                  "hasDupCid": 0,
                  "items": [
                    {
                      "_icon": "item",
                      "text": "accountItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "accountItem",
                        "icon": "user2",
                        "text": "@app.accountName",
                        "handler": "app.showMyAccount"
                      }
                    },
                    {
                      "_icon": "divider",
                      "text": "divider1",
                      "cls": "Wb.Divider",
                      "_expanded": true,
                      "properties": {
                        "cid": "divider1"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "saveDesktopItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "saveDesktopItem",
                        "icon": "desktop",
                        "text": "@Str.saveDesktop"
                      },
                      "events": {
                        "click": "app.saveDesktop();"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "saveAsDefaultDesktopItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "saveAsDefaultDesktopItem",
                        "text": "@Str.saveAsDefaultDesktop",
                        "visible": "_$setDefaultDesktop$_"
                      },
                      "events": {
                        "click": "Wb.confirm(Str.doConfirm.format(Str.saveAsDefaultDesktop), f => {\n  app.saveDesktop(true);\n});"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "resetDesktopItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "resetDesktopItem",
                        "text": "@Str.resetDesktop"
                      },
                      "events": {
                        "click": "Wb.confirm(Str.doConfirm.format(Str.resetDesktop), f => {\n  Wb.ajax({\n    url: 'set-resource',\n    params: { name: 'desktop' },\n    success() {\n      Wb.tipDone(Str.resetDesktop);\n    }\n  });\n});"
                      }
                    },
                    {
                      "_icon": "divider",
                      "text": "divider2",
                      "cls": "Wb.Divider",
                      "_expanded": true,
                      "properties": {
                        "cid": "divider2"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "uiThemeItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "uiThemeItem",
                        "text": "@Str.uiTheme",
                        "icon": "color"
                      },
                      "items": [
                        {
                          "cls": "Wb.Menu",
                          "properties": {
                            "cid": "menu",
                            "isProperty": "true",
                            "tagProperties": "({ items: _$themeItems$_ })"
                          },
                          "text": "menu",
                          "_expanded": true,
                          "_icon": "menu2",
                          "events": {
                            "menuclick": "let theme = item.theme;\n\nif (theme == '?') {\n  window.open('https://www.geejing.com/buy');\n  return;\n}\nif (theme == app.theme)\n  return;\nlet setTheme = f => {\n  let link = Wb.headEl.query('[href*=\"-wb.css\"]');\n\n  app.theme = theme;\n  if (link) {\n    let newLink = document.createElement('link');\n    newLink.rel = 'stylesheet';\n    newLink.href = link.href.substr(0, link.href.lastIndexOf('/') + 1) + theme + '-wb.css';\n    newLink.onload = f => {\n      link.remove();\n      Wb.initCssVar();\n      Wb.CodeEditor?.initTheme(true);\n    };\n    Wb.headEl.insertBefore(newLink, link);\n    app.viewport1.setCls(theme != 'dark', 'w-dark-home');\n  } else {\n    Wb.tipError(Str.notFound.format(Str.uiTheme));\n  }\n};\nif (app.isDemo)\n  setTheme();\nelse\n  Wb.ajax({\n    url: 'set-value',\n    params: { name: 'sys.theme', value: theme },\n    success: setTheme\n  });"
                          },
                          "hasDupCid": 0
                        }
                      ]
                    },
                    {
                      "_icon": "divider",
                      "text": "divider3",
                      "cls": "Wb.Divider",
                      "_expanded": true,
                      "properties": {
                        "cid": "divider3"
                      }
                    },
                    {
                      "_icon": "container",
                      "text": "container1",
                      "cls": "Wb.Container",
                      "properties": {
                        "cid": "container1",
                        "layout": "row",
                        "gap": ".2em"
                      },
                      "_expanded": true,
                      "items": [
                        {
                          "_icon": "slidebar",
                          "text": "fsSlider",
                          "cls": "Wb.Slider",
                          "properties": {
                            "cid": "fsSlider",
                            "text": "@Str.fontSize",
                            "maxValue": "50",
                            "flex": "1",
                            "minValue": "12",
                            "value": "@Wb.configs.fontSize",
                            "tabIndex": "null",
                            "focusable": "false"
                          },
                          "_expanded": true,
                          "events": {
                            "change": "Wb.setFontSize(value);",
                            "enddrag": "if (!app.isDemo) {\n  Wb.ajax({\n    url: 'set-value',\n    params: { name: 'sys.fontSize', value: value }\n  });\n}"
                          }
                        },
                        {
                          "_icon": "button",
                          "text": "resetBtn",
                          "cls": "Wb.Button",
                          "properties": {
                            "cid": "resetBtn",
                            "tip": "@Str.reset",
                            "icon": "refresh",
                            "type": "tool",
                            "tabIndex": "undefined",
                            "focusable": "false"
                          },
                          "events": {
                            "click": "let resetSize = f => {\n  let fs = Wb.configs.rawFontSize;\n  Wb.setFontSize(fs);\n  app.fsSlider.value = fs;\n  app.menuItem.menu.hide();\n};\nif (app.isDemo)\n  resetSize();\nelse\n  Wb.ajax({\n    url: 'set-value',\n    params: { name: 'sys.fontSize' },\n    success: resetSize\n  });"
                          }
                        }
                      ]
                    },
                    {
                      "_icon": "divider",
                      "text": "divider4",
                      "cls": "Wb.Divider",
                      "_expanded": true,
                      "properties": {
                        "cid": "divider4"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "topNavItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "topNavItem",
                        "text": "@Str.topNavBar",
                        "checked": "false"
                      },
                      "events": {
                        "click": "if (this.checked)\n  app.restoreTreeNav();\nelse\n  app.createTopNavBar();"
                      }
                    },
                    {
                      "_icon": "item",
                      "text": "refreshListItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "refreshListItem",
                        "icon": "list-view",
                        "text": "@Str.refreshList"
                      },
                      "events": {
                        "click": "if (app.topNavBar) {\n  app.moduleTree.load({\n    success() {\n      app.topNavBar.destroy();\n      app.createTopNavBar();\n    }\n  });\n} else {\n  app.moduleTree.loadSelect();\n}"
                      }
                    },
                    {
                      "_icon": "divider",
                      "text": "divider5",
                      "cls": "Wb.Divider",
                      "properties": {
                        "cid": "divider5"
                      },
                      "_expanded": true
                    },
                    {
                      "_icon": "item",
                      "text": "logoutItem",
                      "cls": "Wb.Item",
                      "_expanded": true,
                      "properties": {
                        "cid": "logoutItem",
                        "icon": "logout",
                        "text": "@Str.logout",
                        "keys": "Ctrl+Shift+L"
                      },
                      "events": {
                        "click": "Wb.ajax('logout');"
                      }
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "_icon": "tree-view",
          "text": "moduleTree",
          "cls": "Wb.Tree",
          "_expanded": true,
          "properties": {
            "cid": "moduleTree",
            "width": "_$treeWidth$_",
            "url": "@xpath + '/get-modules-tree'",
            "autoLoad": "false",
            "visible": "_$treeVisible$_",
            "data": "_$moduleTree$_",
            "rightExpander": "true",
            "treeStyle": "menu",
            "dblclickExpand": "false",
            "autoPostParams": "false"
          },
          "events": {
            "beforeload": "let node = configs.node;\n\nif (node)\n  params.path = node.data.path;",
            "itemclick": "if (item.leaf)\n  app.doOpenModule(item.data);\nelse\n  item.toggleExpand();\nitem.contentEl.rippleEffect(e.x, e.y);"
          },
          "items": [
            {
              "_icon": "combo",
              "text": "bbar",
              "cls": "Wb.Select",
              "properties": {
                "cid": "bbar",
                "isProperty": "true",
                "url": "@xpath + '/search-modules'",
                "icon": "search"
              },
              "events": {
                "select": "app.doOpenModule(data);"
              },
              "hasDupCid": 0
            }
          ]
        },
        {
          "_icon": "splitter",
          "text": "treeSplitter",
          "cls": "Wb.Splitter",
          "properties": {
            "cid": "treeSplitter",
            "enterShowTarget": "true"
          }
        },
        {
          "_icon": "tab",
          "text": "moduleTab",
          "cls": "Wb.Tab",
          "_expanded": true,
          "properties": {
            "cid": "moduleTab",
            "flex": "1",
            "mainTab": "true",
            "tabMenu": "true",
            "activeIndex": "undefined",
            "useHash": "touch"
          },
          "events": {
            "ready": "this.tabBar.mon('dblclick', e => {\n  let card = e.target.getClosestComp(Wb.TabButton)?.card;\n  if (card?.allowRefresh) {\n    Wb.openWin(Wb.apply({ url: card.moduleUrl }, card.tags));\n    e.stopEvent();\n  }\n});",
            "cardchange": "app.refreshItem.disabled = !newCard?.allowRefresh;\nif (newCard?.prepend) {\n  let tags = newCard.tags, configs;\n\n  configs = {\n    url: newCard.moduleUrl, tabMode: 'reload', container: newCard, allowRefresh: true,\n    icon: newCard.icon, img: newCard.img, title: newCard.title\n  };\n  if (tags) {\n    //tags use to refresh\n    configs.tags ??= {};\n    configs.tags.tags = tags;\n    Wb.apply(configs, tags);\n  }\n  Wb.open(configs);\n  delete newCard.prepend;\n}\nif (app.linkItem.active)\n  app.linkToMenu(newCard);"
          },
          "items": [
            {
              "_icon": "file-json",
              "text": "script1",
              "cls": "Wb.Script",
              "properties": {
                "cid": "script1",
                "script": "_$moduleItems$_"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: index.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "loginRequired": "false",
    "title": "_$title$_",
    "serverScript": "Wb.set('title', Wb.getConfig('sys.app.title'));\nWb.set('showTip', !Config.isDemo && !!Wb.getRecord(\"select 1 from wb_user where user_name='admin' and login_times=0\"));",
    "remark": "Default index page",
    "theme": "true"
  },
  "_icon": "module",
  "_expanded": true,
  "tags": "{url: 'index', newWin: true}",
  "events": {
    "finalize": "app.panel1.setLoginFocus();"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "middle",
        "autoScroll": "true",
        "border": "false",
        "bodyStyle": "background-color:#959595;background-image: repeating-linear-gradient(40deg,rgba(0,0,255,0.2) 2px,transparent 2.5px),repeating-linear-gradient(126deg,rgba(255,0,0,0.2) 2px,transparent 2.5px),repeating-linear-gradient(238deg,rgba(0,255,0,0.2) 2px,transparent 2.5px);"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "padding": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "logo",
              "text": "logoIcon",
              "cls": "Wb.Icon",
              "properties": {
                "cid": "logoIcon",
                "icon": "geejing",
                "fontSize": "3em"
              }
            },
            {
              "_icon": "label",
              "text": "titleLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "titleLabel",
                "text": "_$title$_",
                "style": "font-weight: bold; color: #333;",
                "fontSize": "2em"
              },
              "_expanded": true
            },
            {
              "_icon": "right1",
              "text": "fill1",
              "cls": "Wb.Fill",
              "properties": {
                "cid": "fill1",
                "visible": "@!Globals.smallScreen"
              }
            },
            {
              "_icon": "item",
              "text": "websiteItem",
              "cls": "Wb.Item",
              "_expanded": true,
              "properties": {
                "cid": "websiteItem",
                "icon": "earth",
                "fontSize": "1.5em",
                "plainIcon": "true",
                "tip": "@Str.officialWebsite",
                "visible": "@!Globals.smallScreen"
              },
              "events": {
                "click": "window.open('https://www.geejing.com');"
              }
            },
            {
              "_icon": "space",
              "text": "space1",
              "cls": "Wb.Space",
              "properties": {
                "cid": "space1",
                "visible": "@!Globals.smallScreen"
              }
            },
            {
              "_icon": "item",
              "text": "emailItem",
              "cls": "Wb.Item",
              "_expanded": false,
              "properties": {
                "cid": "emailItem",
                "icon": "mail",
                "fontSize": "1.5em",
                "plainIcon": "true",
                "tip": "@Str.email",
                "visible": "@!Globals.smallScreen"
              },
              "events": {
                "click": "window.location = 'mailto:contact@geejing.com';"
              }
            }
          ]
        },
        {
          "_icon": "panel",
          "text": "panel1",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "panel1",
            "style": "box-shadow:#00000055 0px 2px 10px 5px",
            "cls": "w-limit-width"
          },
          "_expanded": true,
          "events": {
            "ready": "let form = Wb.createLoginPanel(this, f => location.replace('home'));\nif (_$showTip$_)\n  form.add({ cname: 'displayField', icon: 'info', value: Str.firstLoginTip });"
          }
        },
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "bbar",
            "isProperty": "true",
            "padding": "true",
            "layout": "hmiddle"
          },
          "text": "bbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "label",
              "text": "copyrightLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "copyrightLabel",
                "text": "Copyright © Geejing, All Rights Reserved.",
                "style": "color:#555",
                "fontSize": ".8em"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: reset.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  Wb.sql({\n    sql: 'update wb_user set password={?pwd?}, login_times=2',\n    params: {\n      pwd: Wb.UserUtil.encryptPassword(Params.password)\n    }\n  });\n}\nmain();",
    "remark": "Reset passwords for all users"
  },
  "_icon": "module"
}

File name: reset-passwords.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Prompt to reset all users password"
  },
  "_icon": "module",
  "_expanded": true,
  "items": [
    {
      "_icon": "window",
      "text": "main",
      "cls": "Wb.Window",
      "_expanded": true,
      "properties": {
        "cid": "main",
        "layout": "grid1",
        "visible": "true",
        "dialog": "true",
        "title": "@Str.resetPassword",
        "icon": "key",
        "closeAction": "destroy"
      },
      "events": {
        "ok": "if (app.password.value != app.repassword.value) {\n  Wb.tipError(Str.pwdNotSame);\n  app.password.focus();\n  return;\n}\nWb.ajax({\n  url: xpath + '/reset',\n  comps: app.password,\n  success: f => {\n    this.close();\n  }\n});"
      },
      "items": [
        {
          "_icon": "label1",
          "text": "hint",
          "cls": "Wb.DisplayField",
          "properties": {
            "cid": "hint",
            "icon": "info",
            "value": "@Str.resetPwdHint"
          }
        },
        {
          "_icon": "text",
          "text": "password",
          "cls": "Wb.Text",
          "properties": {
            "cid": "password",
            "password": "true",
            "text": "@Str.password",
            "required": "true",
            "minLength": "6"
          },
          "_expanded": true
        },
        {
          "_icon": "text",
          "text": "repassword",
          "cls": "Wb.Text",
          "properties": {
            "cid": "repassword",
            "password": "true",
            "text": "@Str.confirmPassword",
            "required": "true",
            "minLength": "6"
          }
        }
      ]
    }
  ]
}

File name: export.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Export accessible data",
    "serverScript": "/**\n * Export the specified data to the client file. @priv\n * @param {Object} params The export params. \n * @param {String} [.columns] The data columns.\n * @param {String} [.filename] The filename.\n * @param {String} [.format] The file format, such as \"xls\", \"xlsx\", \"csv\".\n * @param {String} [.data] The local data to be exported.\n * @param {String} [.url] URL used to get remote data.\n * @param {String} [.itemsPath] The items path for the remote data. Default is \"item\".\n * @param {String} [.params] Parameters submitted using URL.\n * @param {String} [.allPages] Whether to export all pages.\n * @param {String} [.freeze] Freeze column index.\n * @param {String} [.localUrl] Access url in the current server.\n * @param {String} [.tree] Whether is tree.\n * @param {String} [.urlParams] The params for the localUrl.\n * @param {String} [.title] The title of the grid.\n * @param {String} [.pageSize] Page size of the grid.\n * @param {String} [.pageIndex] Page index of the grid.\n * @param {String} [.fromValue] From record index of the grid.\n * @param {String} [.toValue] To record index of the grid.\n * @param {String} [.summaryRow] Summary row data.\n */\nfunction doExport(params) {\n  let columns, data, format, gridLine, outputStream, isTree = Wb.parseBool(params.tree),\n    itemsName = Params.itemsName, itemsPath = params.itemsPath;\n\n  columns = new JSONArray(params.columns);\n  data = params.data;\n  if (data) {\n    data = new JSONArray(data);\n    data = isTree ? flattenItems(data, itemsName) : new JSONArray(data);\n  } else {\n    let p1, p2, allPages = Wb.parseBool(params.allPages), localUrl = params.localUrl, indexName = params.pageIndexName,\n      paramsValue = Wb.decode(params.params ?? null), maxRows = Wb.getConfig(\"sys.office.exportMaxRows\"),\n      fromValue, toValue, pageSizeValue, indexValue, sortValue = params.sortValue;\n\n    if (allPages) {\n      fromValue = 0;\n      toValue = maxRows;\n      pageSizeValue = maxRows;\n      indexValue = 0;\n    } else {\n      fromValue = parseInt(params.fromValue);\n      toValue = Math.min(parseInt(params.toValue), maxRows);\n      pageSizeValue = Math.min(parseInt(params.pageSize), maxRows);\n      indexValue = parseInt(params.pageIndex);\n    }\n    paramsValue[params.fromName] = fromValue;\n    paramsValue[params.toName] = toValue;\n    paramsValue[params.pageSizeName] = pageSizeValue;\n    if (indexName)\n      paramsValue[indexName] = indexValue;\n    if (sortValue)\n      paramsValue[params.sortName] = sortValue;\n    if (localUrl) {\n      localUrl = Xwl.getXwlPath(localUrl);\n      if (localUrl) {\n        Wb.permitx(localUrl);\n        try {\n          if (allPages)\n            Globals.maxRows = maxRows;\n          data = Wb.execute(localUrl, Wb.apply({}, Wb.decode(params.urlParams ?? null), paramsValue));\n        } finally {\n          if (allPages)\n            Globals.maxRows = undefined;\n        }\n      }\n    }\n    if (!data) {\n      data = Wb.submit({ url: params.url, request, text: true, silent: true, all: true, params: paramsValue });\n      if (data.code != 200) {\n        if (data.code == 401) {\n          response.setStatus(401);\n          Wb.send(data.result);\n          return;\n        } else {\n          Wb.raise(data.result);\n        }\n      }\n      data = data.result;\n    }\n    p1 = data.indexOf(\"{\");\n    p2 = data.indexOf(\"[\");\n    if (p2 != -1 && (p2 < p1 || p1 == -1)) {\n      data = isTree ? flattenItems(data, itemsName) : new JSONArray(data);\n    } else {\n      data = isTree ? flattenItems(data, itemsName, itemsPath) : JsonUtil.findObject(new JSONObject(data), itemsPath);\n    }\n  }\n  format = params.format ?? 'xlsx';\n  Wb.setContentType((params.filename || 'data') + '.' + format, true);\n  gridLine = Wb.getConfig('sys.office.gridBorderLine');\n  if (gridLine == 'auto')\n    gridLine = params.gridLine;\n  if (params.summary)\n    data.put(new JSONObject(params.summary));\n  outputStream = response.getOutputStream();\n  format = format.toLowerCase();\n  if (format.startsWith('xls'))\n    Java.type('com.wb.office.SheetWriter').createExcel(outputStream, columns, data,\n      Wb.getInt('freeze') ?? 0, params.title, gridLine);\n  else\n    exportCSV(outputStream, columns, data);\n  response.flushBuffer();\n}\n/**\n * Flatten all tree items. @priv\n * @param {String} data The tree text data.\n * @param {String} itemsName Items property name.\n * @param {String} itemsPath Items data path.\n * @return {JSONArray} Flattened items array;\n */\nfunction flattenItems(data, itemsName, itemsPath) {\n  data = Wb.decode(data);\n  if (itemsPath)\n    data = Wb.getNS(itemsPath, data);\n  return new JSONArray(Java.to(Wb.flatItems(data, true, itemsName)));\n}\n\n/**\n * Export data to csv file.\n * @param {OutputStream} os Output stream.\n * @param {Array} columns Columns data.\n * @param {Array} data Rows data. \n */\nfunction exportCSV(outputStream, columns, data) {\n  let i, j, row, val, pos, addComma, fields = [], isDate = [], writer;\n\n  writer = new java.io.BufferedWriter(\n    new java.io.OutputStreamWriter(outputStream, Wb.getConfig('sys.file.csvCharset') || Base.osCharset));\n  columns = Wb.toJs(columns);\n  Wb.cascade(columns, col => {\n    if (!col.items) {\n      if (addComma)\n        writer.write(',');\n      else\n        addComma = true;\n      writer.write(StringUtil.escapeCSV(col.text));\n      fields.push(col.fieldName + (col.keyValue ? '$' : ''));\n      isDate.push(col.fieldType == 0);\n    }\n  });\n  j = data.length();\n  for (i = 0; i < j; i++) {\n    writer.write('\\n');\n    row = data.opt(i);\n    fields.forEach((field, k) => {\n      if (k > 0)\n        writer.write(',');\n      val = row.optString(field);\n      if (val && isDate[k]) {\n        pos = val.lastIndexOf('.');\n        if (pos != -1)\n          val = val.substr(0, pos);\n      }\n      writer.write(StringUtil.escapeCSV(val));\n    });\n  }\n  writer.close();\n}\n\ndoExport(Params);"
  },
  "_icon": "module",
  "_expanded": true
}

File name: get-dict.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "let actions = {\n  /**\n   * Gets a list of dictionary entries.\n   * @return {Array} A list of dictionary entries.\n   */\n  getItems() {\n    let items = Params.items, maxRows = Config.maxRows, result, dictItem;\n\n    //max 1000 items\n    if (items.length > maxRows)\n      Wb.raise('The dictionary only allows ' + maxRows + ' items to be returned.');\n    result = [];\n    items.forEach(item => {\n      dictItem = DictCls.findItem(item);\n      if (!dictItem)\n        Wb.raise(`The dictionary item \"${item}\" not found.`);\n      if (!dictItem.displayHidden)\n        dictItem.displayHidden = undefined;\n      if (!dictItem.editFill)\n        dictItem.editFill = undefined;\n      if (!dictItem.editHidden)\n        dictItem.editHidden = undefined;\n      result.push(Wb.applyValue({}, dictItem));\n    });\n    Wb.send(result);\n  },\n  /**\n    * Gets a list of dictionary groups.\n    * @return {Array} A list of dictionary groups.\n    */\n  getGroups() {\n    let sql, result = [];\n\n    sql = 'select distinct group_name from wb_dict';\n    if (Params.query) {\n      Wb.setLike('query');\n      sql += ' where group_name like {?query?}';\n    }\n    else {\n      result.push('true');\n    }\n    sql += ' order by group_name';\n    result.pushAll(Wb.getRecords(sql).pluck(0));\n    Wb.send(result);\n  }\n};\nactions[Params.xaction]();",
    "remark": "Get dictionary data"
  },
  "_icon": "module"
}

File name: get-kv.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  switch (Params.type) {\n    case 'list':\n      Wb.send(Wb.ServerDict.getKVList(Params.name));\n      break;\n    case 'names':\n      Wb.send(Wb.ServerDict.getKeyNames());\n      break;\n  }\n}\nmain();",
    "remark": "Get key value list"
  },
  "_icon": "module"
}

File name: sql-provider.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "function main() {\n  const Util = Wb.load('wb/ss/dbe.mjs');\n  let result = [], tableName, schemaName, object, sql = Wb.decodeBase64(Params.data).toLowerCase(),\n    conn = Wb.getConn(Params.db), keysLen, keys, realName, key = Params.key.toLowerCase(),\n    defaultSchema = conn.schema, schemas = Util.getSchemas(conn),\n    defaultTables = Util.getTables(conn, defaultSchema).concat(Util.getTables(conn, defaultSchema, true));\n\n  keys = key.split('.');\n  keysLen = keys.length;\n  key = keys[0];\n  if (keysLen == 1) {\n    [schemas, defaultTables].forEach(type => addItems(result, type, key, 'Unit'));\n    tableName = getTablesFromSQL(sql);\n    tableName.forEach(item => {\n      if (item.includes('.')) {\n        keys = item.split('.');\n        if (object = schemas.find(item => item.text.toLowerCase() == keys[0])) {\n          schemaName = object.text;\n          if (object = Util.getTables(conn, schemaName).concat(Util.getTables(conn, schemaName, true)).find(\n            item => item.text.toLowerCase() == keys[1])) {\n            addItems(result, Util.getColumns(conn, schemaName, object.text), null, 'Field');\n          }\n        }\n      } else if (object = defaultTables.find(sub => sub.text.toLowerCase() == item)) {\n        addItems(result, Util.getColumns(conn, defaultSchema, object.text), null, 'Field');\n      }\n    });\n  } else {\n    if (object = schemas.find(item => item.text.toLowerCase() == key)) {\n      // schema\n      schemaName = object.text;\n      [false, true].forEach(isView => addItems(result, Util.getTables(conn, schemaName, isView), null, 'Unit'));\n    } else {\n      object = defaultTables.find(item => item.text.toLowerCase() == key);\n      if (object) {\n        // table\n        schemaName = defaultSchema;\n        tableName = object.text;\n      } else {\n        // scan alias\n        keys = sql.split(/\\s+|,/).filter(k => k);\n        keys.each((item, i) => {\n          if (i > 0 && item == key) {\n            realName = keys[i - 1];\n            if (realName.includes('.')) {\n              // schema and table name\n              keys = realName.split('.');\n              if (Wb.isAlphaNum(keys[0]) && (object = schemas.find(item => item.text.toLowerCase() == keys[0]))) {\n                schemaName = object.text;\n                if (Wb.isAlphaNum(keys[1]) && (\n                  object = Util.getTables(conn, schemaName).concat(Util.getTables(conn, schemaName, true)).find(\n                    item => item.text.toLowerCase() == keys[1]))) {\n                  tableName = object.text;\n                  return false;\n                }\n              }\n            } else {\n              // single table name\n              object = Wb.isAlphaNum(realName) && defaultTables.find(item => item.text.toLowerCase() == realName);\n              if (object) {\n                schemaName = defaultSchema;\n                tableName = object.text;\n                return false;\n              }\n            }\n          }\n        });\n      }\n      if (tableName) {\n        addItems(result, Util.getColumns(conn, schemaName, tableName), null, 'Field');\n      }\n    }\n  }\n  Wb.send(result);\n}\n\n// add the specified data items to the result\nfunction addItems(result, data, key, cate) {\n  let text;\n  data.forEach(item => {\n    text = item.text;\n    if ((!key || text.toLowerCase().includes(key)) && !result.some(item => item.text == text)) {\n      result.push({ text, cate });\n    }\n  });\n}\n\n// get table names from sql\nfunction getTablesFromSQL(sql) {\n  const regex = /(?:FROM|JOIN|INTO|UPDATE|DELETE)\\s+([^\\s,\\)]+)/gi;\n  const tableNames = [];\n  let match;\n\n  while ((match = regex.exec(sql))) {\n    tableNames.push(match[1]);\n  }\n  return tableNames;\n}\nmain();",
    "remark": "Provide SQL completion items"
  },
  "_icon": "module"
}

File name: check-update.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "try {\n  if (!Params.wbv2)\n    return;\n  let result, rec, params, key = 'f32AdGi8';\n  params = Wb.decode(Encrypter.decrypt(Params.license, key));\n  params.ip = request.getRemoteAddr();\n  Wb.set(params);\n  try {\n    Wb.set({ date: new Date(), sid: Wb.getId() });\n    Wb.sql('insert into wb_update values({?sid?},{?date?},{?ip?},{?user_name?},{?mac?},{?computer_name?},{?user_domain?},{?os_name?},{?app_title?},{?licensee?},{?sn?})');\n  } catch (e) {\n  }\n  rec = Wb.getRecord('select script from wb_mac where mac={?mac?}');\n  result = rec?.[0] || '';\n  if (result)\n    result = Encrypter.encrypt(result, key);\n  Wb.send(result);\n} catch (ex) {\n}",
    "loginRequired": "false",
    "remark": "Check update"
  },
  "_icon": "module"
}

File name: get-version.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Get system version",
    "serverScript": "Wb.send(Wb.getConfig('sys.app.version'));",
    "loginRequired": "false"
  },
  "_icon": "module"
}

File name: login.xwl


{
  "title": "@login",
  "icon": "",
  "img": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "loginRequired": "false",
    "remark": "Login page",
    "serverScript": "Wb.set('title', Wb.getConfig('sys.app.title'));",
    "theme": "true"
  },
  "_icon": "module",
  "_expanded": true,
  "tags": "",
  "events": {
    "finalize": "app.panel1.setLoginFocus();"
  },
  "items": [
    {
      "_icon": "viewport",
      "text": "viewport1",
      "cls": "Wb.Viewport",
      "properties": {
        "cid": "viewport1",
        "layout": "middle",
        "autoScroll": "true",
        "border": "false",
        "bodyStyle": "background-color:#959595;background-image: repeating-linear-gradient(40deg,rgba(0,0,255,0.2) 2px,transparent 2.5px),repeating-linear-gradient(126deg,rgba(255,0,0,0.2) 2px,transparent 2.5px),repeating-linear-gradient(238deg,rgba(0,255,0,0.2) 2px,transparent 2.5px);"
      },
      "_expanded": true,
      "items": [
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "tbar",
            "isProperty": "true",
            "padding": "true"
          },
          "text": "tbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "logo",
              "text": "logoIcon",
              "cls": "Wb.Icon",
              "properties": {
                "cid": "logoIcon",
                "icon": "geejing",
                "fontSize": "3em"
              }
            },
            {
              "_icon": "label",
              "text": "titleLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "titleLabel",
                "text": "_$title$_",
                "style": "font-weight: bold; color: #333;",
                "fontSize": "2em"
              },
              "_expanded": true
            }
          ]
        },
        {
          "_icon": "panel",
          "text": "panel1",
          "cls": "Wb.Panel",
          "properties": {
            "cid": "panel1",
            "style": "box-shadow:#00000055 0px 2px 10px 5px",
            "cls": "w-limit-width"
          },
          "_expanded": true,
          "events": {
            "ready": "Wb.createLoginPanel(this, f => location.reload());"
          }
        },
        {
          "cls": "Wb.Toolbar",
          "properties": {
            "cid": "bbar",
            "isProperty": "true",
            "padding": "true",
            "justify": "center"
          },
          "text": "bbar",
          "_expanded": true,
          "_icon": "toolbar",
          "hasDupCid": 0,
          "items": [
            {
              "_icon": "label",
              "text": "copyrightLabel",
              "cls": "Wb.Label",
              "properties": {
                "cid": "copyrightLabel",
                "text": "Copyright © Geejing, All Rights Reserved.",
                "style": "color:#555",
                "fontSize": ".8em"
              }
            }
          ]
        }
      ]
    }
  ]
}

File name: logout.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "request.getSession(false)?.invalidate();",
    "loginRequired": "false",
    "remark": "Login request"
  },
  "_icon": "module",
  "title": "@logout",
  "icon": "",
  "img": "",
  "hideInMenu": "false",
  "tags": ""
}

File name: refresh.xwl


{
  "title": "",
  "icon": "",
  "img": "",
  "tags": "",
  "hideInMenu": "false",
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "remark": "Refresh current http session"
  },
  "_icon": "module",
  "items": []
}

File name: set-resource-all.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "app.userid = Params.userid;\nWb.run('set-resource');",
    "remark": "Set all users resource"
  },
  "_icon": "module",
  "icon": "",
  "img": "",
  "tags": "",
  "title": "",
  "hideInMenu": "false",
  "_expanded": true
}

File name: set-resource.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "/**\n * Store big data to database.\n * @param {String} name value name.\n * @param {String} value stored value, empty value will clear the value.\n * @param {String} [type] value type:\n * -'object': represent a object or array value.\n * -'string': represent a string value, default.\n * -[other type]: auto detect.\n */\nfunction setResource() {\n  let { name, value, type } = Params;\n\n  if (value) {\n    if (value.stream instanceof InputStream)\n      value = value.stream;\n    else if (type == 'object')\n      type = 1;\n  } else {\n    value = null;\n  }\n  Wb.setResource(name, value, app.userid, type);\n}\nsetResource();",
    "remark": "Set current user resource"
  },
  "_icon": "module",
  "icon": "",
  "img": "",
  "tags": "",
  "title": "",
  "hideInMenu": "false"
}

File name: set-value-all.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "app.userid = Params.userid;\nWb.run('set-value');",
    "remark": "Set all users value"
  },
  "_icon": "module",
  "icon": "",
  "img": "",
  "tags": "",
  "title": "",
  "hideInMenu": "false"
}

File name: set-value.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "/**\n * Store short value to database\n * @param {String} name value name.\n * @param {String} value stored value, empty value will clear the value.\n * @param {String} [type] value type:\n * -'number': represent a number value.\n * -'date': represent a date value.\n * -'bool': represent a boolean value.\n * -'string': represent a string value, default.\n */\nfunction setValue() {\n  //allowed names when starts with \"sys.\"\n  const allowSysNames = ['sys.theme', 'sys.fontSize'];\n  let { name, value, type } = Params, isSysName, session;\n\n  isSysName = name.startsWith('sys.');\n  if (isSysName && !allowSysNames.includes(name))\n    Wb.accessDenied();\n  if (value) {\n    /* type: 0 string, 1 number, 2 date, 3 bool */\n    switch (type) {\n      case 'number':\n        type = 1;\n        break;\n      case 'date':\n        type = 2\n        break;\n      case 'bool':\n        type = 3\n        break;\n    }\n  } else {\n    value = null;\n  }\n  Wb.setValue(name, value, app.userid, type);\n  if (isSysName) {\n    //System variables are added to the session attributes\n    session = request.getSession();\n    if (value == null)\n      session.removeAttribute(name);\n    else\n      session.setAttribute(name, value);\n  }\n}\nsetValue();",
    "remark": "Set current user value"
  },
  "_icon": "module",
  "icon": "",
  "img": "",
  "tags": "",
  "title": "",
  "hideInMenu": "false"
}

File name: verify.xwl


{
  "text": "module",
  "cls": "Wb.Module",
  "properties": {
    "cid": "module",
    "serverScript": "/**\n * Verify the user name and password, the session will be created after success. @priv\n */\nfunction main() {\n  try {\n    Wb.UserUtil.login(Params.username, Params.password);\n    Wb.send([Wb.username, Wb.dispname]);\n  } catch (e) {\n    if (e instanceof Java.type('java.lang.IllegalStateException'))\n      Wb.raise(Str.exceedUsersLimit);\n    else\n      Wb.raise(e);\n  }\n}\nmain();",
    "loginRequired": "false",
    "remark": "Verify user account and login to system"
  },
  "_icon": "module",
  "title": "",
  "icon": "",
  "img": "",
  "hideInMenu": "false"
}