[
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"daily\"\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [\"version-update:semver-minor\", \"version-update:semver-patch\"]\n"
  },
  {
    "path": ".github/workflows/CI.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - master\n    tags:\n      - 'release-*'\n    paths:\n      - 'luci-app-wrtbwmon/**'\n  pull_request:\n    branches:\n      - master\n    paths:\n      - 'luci-app-wrtbwmon/**'\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    name: Build the IPK\n    runs-on: ubuntu-latest\n    container:\n      image: ghcr.io/openwrt/sdk:x86-64-23.05-SNAPSHOT\n      options: --user root\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v4\n    - name: Docker Build\n      working-directory: /builder\n      run: |\n        sed \\\n        \t-e 's,git\\.openwrt\\.org/feed/,github.com/openwrt/,' \\\n        \t-e 's,git\\.openwrt\\.org/openwrt/,github.com/openwrt/,' \\\n        \t-e 's,git\\.openwrt\\.org/project/,github.com/openwrt/,' \\\n        \tfeeds.conf.default | grep -Ev \"^src-git(-full)? (routing|telephony) .*\" > feeds.conf\n\n        echo \"src-cpy local ${GITHUB_WORKSPACE}\" >> feeds.conf\n\n        ./scripts/feeds update -f luci local\n        ./scripts/feeds install -p local luci-app-wrtbwmon\n\n        make defconfig\n        make package/luci-app-wrtbwmon/compile V=sc -j$(nproc) BUILD_LOG=1\n        tar -cJf logs.tar.xz logs\n    - name: Release\n      if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/release-')\n      uses: actions/github-script@v7\n      with:\n        script: |\n          const tag = context.ref.replace(\"refs/tags/\", \"\");\n          try {\n            // Get release for this tag\n            const release = await github.rest.repos.getReleaseByTag({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag: tag,\n            });\n            // Delete obsolete release\n            await github.rest.repos.deleteRelease({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              release_id: release.data.id,\n            });\n          }\n          catch(err) {\n            console.log(err);\n          }\n          finally {\n            // Create release for this tag\n            const release = await github.rest.repos.createRelease({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag_name: tag,\n              draft: false,\n              prerelease: true,\n            });\n            // Upload the release asset\n            const fs = require('fs');\n            const patterns = ['/builder/bin/packages/x86_64/*/*wrtbwmon*.ipk']\n            const globber = await glob.create(patterns.join('\\n'))\n            for await (const file of globber.globGenerator()) {\n              await github.rest.repos.uploadReleaseAsset({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                release_id: release.data.id,\n                name: file.substr(file.lastIndexOf('/') + 1),\n                data: await fs.readFileSync(file)\n              });\n            }\n          }\n    - name: Upload app\n      if: ${{ ! (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/release-')) }}\n      uses: actions/upload-artifact@v4\n      with:\n        name: luci-app-wrtbwmon\n        path: /builder/bin/packages/x86_64/*/*wrtbwmon*\n        if-no-files-found: error\n    - name: Upload Log\n      if: ${{ always() }}\n      uses: actions/upload-artifact@v4\n      with:\n        name: buildlog\n        path: /builder/logs.tar.xz\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: \"Close stale issues\"\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/stale@v9\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        stale-issue-message: 'This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days'\n        stale-pr-message: 'This pr is stale has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days'\n        stale-issue-label: 'no-issue-activity'\n        exempt-issue-labels: 'awaiting-approval,work-in-progress'\n        stale-pr-label: 'no-pr-activity'\n        exempt-pr-labels: 'awaiting-approval,work-in-progress'\n        days-before-stale: 120\n        days-before-close: 5\n"
  },
  {
    "path": "README.md",
    "content": "# luci-app-wrtbwmon\n\n[![CI](https://github.com/brvphoenix/luci-app-wrtbwmon/workflows/CI/badge.svg)](https://github.com/brvphoenix/luci-app-wrtbwmon/actions)\n[![GitHub All Releases](https://img.shields.io/github/downloads/brvphoenix/luci-app-wrtbwmon/total)](https://github.com/brvphoenix/luci-app-wrtbwmon/releases)\n[![Lastest Release](https://img.shields.io/github/v/release/brvphoenix/luci-app-wrtbwmon.svg?logo=github&cacheSeconds=10&label=latest)](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/latest)\n\nThis repo provides yet another LuCI module for wrtbwmon, which has similar features with [Kiougar's one](https://github.com/Kiougar/luci-wrtbwmon). The differnence is that this one has more features supported:\n1. Support IPV6.\n1. Identify a host by the unique MAC rather than its IP.\n1. Use the progress bar to display the total bandwidth.\n1. For brevity, some columns are hidden by default.\n1. Convert to client side for rendering just as what the new openwrt release has done.\n\n## Known issues\n* **Incompatible** with the [pyrovski's wrtbwmon](https://github.com/pyrovski/wrtbwmon). **You must download the compatible one from [here](https://github.com/brvphoenix/wrtbwmon)**.\n* **Incompatible** with Routing/NAT, Flow Offloading and so on.\n\n## Screenshots\n![Screenshots](https://github.com/brvphoenix/luci-app-wrtbwmon/blob/master/screenshot.png?raw=true)\n\n## Downloading\nOpenwrt 19.07 has been fully supported after commit: [ff4909d](https://github.com/brvphoenix/luci-app-wrtbwmon/tree/ff4909d8f5d06fee87f7ec5a365ac5dde6492130).\n* `openwrt-19.07.3 ... latest`: [latest](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/latest)\n* `openwrt-19.07.0 ... 19.07.2`: [release-2.0.7](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/tag/release-2.0.7)\n* `openwrt-18.06`: [release-1.6.3](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/tag/release-1.6.3)\n\nAfter installing, you will see a new `Traffic status` menu item  in the `Network` menu list in the LuCI Page.\n\n## Information\nIn principle, the lua version (based on the old openwrt 18.06) has been dropped support since [ff4909d](https://github.com/brvphoenix/luci-app-wrtbwmon/tree/ff4909d8f5d06fee87f7ec5a365ac5dde6492130), and the new features will not backport to the old lua version. However, it is welcomed if someone can implement it and make a pr.\n\nIf anyone would like to help translate this luci app, just upload the translation files or make a pr.\n\n## Credits\nThanks to\n* [Kiougar](https://github.com/Kiougar/luci-wrtbwmon) for the original codes.\n* [pyrovski](https://github.com/pyrovski) for creating `wrtbwmon`.\n"
  },
  {
    "path": "luci-app-wrtbwmon/Makefile",
    "content": "#\n# Copyright (C) 2020 xxx <xxx@xxx.com>\n#\n# This is free software, licensed under the Apache License, Version 2.0 .\n#\n\ninclude $(TOPDIR)/rules.mk\n\nPKG_NAME:=luci-app-wrtbwmon\nPKG_VERSION:=2.0.13\n\nPKG_LICENSE:=Apache-2.0\nPKG_MAINTAINER:=\n\nLUCI_TITLE:=A Luci module that uses wrtbwmon to track bandwidth usage\nLUCI_DEPENDS:=+wrtbwmon\nLUCI_PKGARCH:=all\n\ninclude $(TOPDIR)/feeds/luci/luci.mk\n\n# call BuildPackage - OpenWrt buildroot signature\n"
  },
  {
    "path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js",
    "content": "'use strict';\n'require form';\n'require rpc';\n'require view';\n\nvar callChangeDatabasePath = rpc.declare({\n\tobject: 'luci.wrtbwmon',\n\tmethod: 'change_db_path',\n\tparams: [ 'state' ]\n});\n\nreturn view.extend({\n\trender: function() {\n\t\tvar m, s, o;\n\n\t\tm = new form.Map('wrtbwmon', _('Usage - Configuration'));\n\n\t\ts = m.section(form.NamedSection, 'general', 'wrtbwmon', _('General settings'));\n\t\ts.addremove = false;\n\n\t\to = s.option(form.Flag, 'enabled', _('Keep running in the background'));\n\t\to.rmempty = true;\n\n\t\to = s.option(form.Value, 'path', _('Database path'), _('This box is used to select the Database path, which is /tmp/usage.db by default.'));\n\t\to.value('/tmp/usage.db');\n\t\to.value('/etc/usage.db');\n\t\to.default = '/tmp/usage.db';\n\t\to.rmempty = false;\n\n\t\treturn m.render();\n\t},\n\n\thandleSaveApply: function(ev, mode) {\n\t\treturn callChangeDatabasePath('before')\n\t\t.then(this.super.bind(this, 'handleSaveApply', arguments))\n\t\t.then(callChangeDatabasePath.bind(this, 'after'));\n\t}\n});\n"
  },
  {
    "path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js",
    "content": "'use strict';\n'require fs';\n'require ui';\n'require view';\n\nreturn view.extend({\n\tload: function() {\n\t\treturn fs.trimmed('/etc/wrtbwmon.user').catch(function(err) {\n\t\t\tui.addNotification(null, E('p', {}, _('Unable to load the customized hostname file: ' + err.message)));\n\t\t\treturn '';\n\t\t});\n\t},\n\n\trender: function(data) {\n\t\treturn E('div', {'class': 'cbi-map'}, [\n\t\t\tE('h2', {'name': 'content'}, [ _('Usage - Custom User File') ]),\n\t\t\tE('div', {'class': 'cbi-map-descr'}, [\n\t\t\t\t_('Each line must have the following format:'),\n\t\t\t\tE('em', {'style': 'color:red'}, '00:aa:bb:cc:ee:ff,hostname')\n\t\t\t]),\n\t\t\tE('div', {'class': 'cbi-section'}, [\n\t\t\t\tE('textarea', {\n\t\t\t\t\t'id': 'custom_hosts',\n\t\t\t\t\t'style': 'width: 100%;padding: .5em;',\n\t\t\t\t\t'rows': 20\n\t\t\t\t}, data)\n\t\t\t])\n\t\t]);\n\t},\n\n\thandleSave: function(ev) {\n\t\tvar map = document.querySelector('#custom_hosts');\n\t\treturn fs.write('/etc/wrtbwmon.user', map.value.trim().replace(/\\r\\n/g, '\\n') + '\\n');\n\t},\n\thandleSaveApply: null,\n\thandleReset: null\n});\n"
  },
  {
    "path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js",
    "content": "'use strict';\n'require dom';\n'require fs';\n'require poll';\n'require rpc';\n'require ui';\n'require validation';\n'require view';\n\nvar cachedData = [];\nvar luciConfig = '/etc/luci-wrtbwmon.conf';\nvar hostNameFile = '/etc/wrtbwmon.user';\nvar columns = {\n\tthClient: _('Clients'),\n\tthMAC: _('MAC'),\n\tthDownload: _('Download'),\n\tthUpload: _('Upload'),\n\tthTotalDown: _('Total Down'),\n\tthTotalUp: _('Total Up'),\n\tthTotal: _('Total'),\n\tthFirstSeen: _('First Seen'),\n\tthLastSeen: _('Last Seen')\n};\n\nvar callLuciDHCPLeases = rpc.declare({\n\tobject: 'luci-rpc',\n\tmethod: 'getDHCPLeases',\n\texpect: { '': {} }\n});\n\nvar callLuciDSLStatus = rpc.declare({\n\tobject: 'luci-rpc',\n\tmethod: 'getDSLStatus',\n\texpect: { '': {} }\n});\n\nvar callGetDatabaseRaw = rpc.declare({\n\tobject: 'luci.wrtbwmon',\n\tmethod: 'get_db_raw',\n\tparams: [ 'protocol' ]\n});\n\nvar callGetDatabasePath = rpc.declare({\n\tobject: 'luci.wrtbwmon',\n\tmethod: 'get_db_path',\n\tparams: [ 'protocol' ]\n});\n\nvar callRemoveDatabase = rpc.declare({\n\tobject: 'luci.wrtbwmon',\n\tmethod: 'remove_db',\n\tparams: [ 'protocol' ]\n});\n\nfunction $(tid) {\n\treturn document.getElementById(tid);\n}\n\nfunction clickToResetDatabase(settings, table, updated, updating, ev) {\n\tif (confirm(_('This will delete the database file. Are you sure?'))) {\n\t\treturn callRemoveDatabase(settings.protocol)\n\t\t.then(function() {\n\t\t\tupdateData(settings, table, updated, updating, true);\n\t\t});\n\t}\n}\n\nfunction clickToSaveConfig(keylist, cstrs) {\n\tvar data = {};\n\n\tfor (var i = 0; i < keylist.length; i++) {\n\t\tdata[keylist[i]] = cstrs[keylist[i]].getValue();\n\t}\n\n\tui.showModal(_('Configuration'), [\n\t\tE('p', { 'class': 'spinning' }, _('Saving configuration data...'))\n\t]);\n\n\treturn fs.write(luciConfig, JSON.stringify(data, undefined, '\\t') + '\\n')\n\t.catch(function(err) {\n\t\tui.addNotification(null, E('p', {}, [ _('Unable to save %s: %s').format(luciConfig, err) ]));\n\t})\n\t.then(ui.hideModal)\n\t.then(function() { document.location.reload(); });\n}\n\nfunction clickToSelectInterval(settings, updating, ev) {\n\tif (ev.target.value > 0) {\n\t\tsettings.interval = parseInt(ev.target.value);\n\t\tif (!poll.active()) poll.start();\n\t}\n\telse {\n\t\tpoll.stop();\n\t\tsetUpdateMessage(updating, -1);\n\t}\n}\n\nfunction clickToSelectProtocol(settings, table, updated, updating, ev) {\n\tsettings.protocol = ev.target.value;\n\tupdateData(settings, table, updated, updating, true);\n}\n\nfunction createOption(args, val) {\n\tvar cstr = args[0], title = args[1], desc = args.slice(-1), widget, frame;\n\twidget = args.length == 4 ? new cstr(val, args[2]) : new cstr(val, args[2], args[3]);\n\n\tframe = E('div', {'class': 'cbi-value'}, [\n\t\tE('label', {'class': 'cbi-value-title'}, title),\n\t\tE('div', {'class': 'cbi-value-field'}, E('div', {}, widget.render()))\n\t]);\n\n\tif (desc && desc != '')\n\t\tdom.append(frame.lastChild, E('div', { 'class': 'cbi-value-description' }, desc));\n\n\treturn [widget, frame];\n}\n\nfunction displayTable(tb, settings) {\n\tvar elm, elmID, col, sortedBy, flag, IPVer;\n\n\telm = tb.querySelector('.th.sorted');\n\telmID = elm ? elm.id : 'thTotal';\n\tsortedBy = elm && elm.classList.contains('ascent') ? 'asc' : 'desc';\n\n\tcol = Object.keys(columns).indexOf(elmID);\n\tIPVer = col == 0 ? settings.protocol : null;\n\tflag = sortedBy == 'desc' ? 1 : -1;\n\n\tcachedData[0].sort(sortTable.bind(this, col, IPVer, flag));\n\n\t//console.time('show');\n\tupdateTable(tb, cachedData, '<em>%s</em>'.format(_('Collecting data...')), settings);\n\t//console.timeEnd('show');\n\tprogressbar('downstream', cachedData[1][0], settings.downstream, settings.useBits, settings.useMultiple);\n\tprogressbar('upstream', cachedData[1][1], settings.upstream, settings.useBits, settings.useMultiple);\n}\n\nfunction formatSize(size, useBits, useMultiple) {\n\t// String.format automatically adds the i for KiB if the multiple is 1024\n\treturn String.format('%%%s.2m%s'.format(useMultiple, (useBits ? 'bit' : 'B')), useBits ? size * 8 : size);\n}\n\nfunction formatSpeed(speed, useBits, useMultiple) {\n\treturn formatSize(speed, useBits, useMultiple) + '/s';\n}\n\nfunction formatDate(d) {\n\tvar Y = d.getFullYear(), M = d.getMonth() + 1, D = d.getDate(),\n\t    hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds();\n\treturn '%04d/%02d/%02d %02d:%02d:%02d'.format(Y, M, D, hh, mm, ss);\n}\n\nfunction getDSLBandwidth() {\n\treturn callLuciDSLStatus().then(function(res) {\n\t\treturn {\n\t\t\tupstream : res.max_data_rate_up || null,\n\t\t\tdownstream : res.max_data_rate_down || null\n\t\t};\n\t});\n}\n\nfunction handleConfig(ev) {\n\tui.showModal(_('Configuration'), [\n\t\t\tE('p', { 'class': 'spinning' }, _('Loading configuration data...'))\n\t]);\n\n\tparseDefaultSettings(luciConfig)\n\t.then(function(settings) {\n\t\tvar arglist, keylist = Object.keys(settings), res, cstrs = {}, node = [], body;\n\n\t\targlist = [\n\t\t\t[ui.Select, _('Default Protocol'), {'ipv4': _('ipv4'), 'ipv6': _('ipv6')}, {}, ''],\n\t\t\t[ui.Select, _('Default Refresh Interval'), {'-1': _('Disabled'), '2': _('2 seconds'), '5': _('5 seconds'), '10': _('10 seconds'), '30': _('30 seconds')}, {sort: ['-1', '2', '5', '10', '30']}, ''],\n\t\t\t[ui.Dropdown, _('Default Columns'), columns, {multiple: true, sort: false, custom_placeholder: '', dropdown_items: 3}, ''],\n\t\t\t[ui.Checkbox, _('Show Zeros'), {value_enabled: true, value_disabled: false}, ''],\n\t\t\t[ui.Checkbox, _('Transfer Speed in Bits'), {value_enabled: true, value_disabled: false}, ''],\n\t\t\t[ui.Select, _('Multiple of Unit'), {'1000': _('SI - 1000'), '1024': _('IEC - 1024')}, {}, ''],\n\t\t\t[ui.Checkbox, _('Use DSL Bandwidth'), {value_enabled: true, value_disabled: false}, ''],\n\t\t\t[ui.Textfield, _('Upstream Bandwidth'), {datatype: 'ufloat'}, 'Mbps'],\n\t\t\t[ui.Textfield, _('Downstream Bandwidth'), {datatype: 'ufloat'}, 'Mbps'],\n\t\t\t[ui.DynamicList, _('Hide MAC Addresses'), '', {datatype: 'macaddr'}, '']\n\t\t]; // [constructor, label(, all_choices), options, description]\n\n\t\tfor (var i = 0; i < keylist.length; i++) {\n\t\t\tres = createOption(arglist[i], settings[keylist[i]]);\n\t\t\tcstrs[keylist[i]] = res[0];\n\t\t\tnode.push(res[1]);\n\t\t}\n\n\t\tbody = [\n\t\t\tE('p', {}, _('Configure the default values for luci-app-wrtbwmon.')),\n\t\t\tE('div', {}, node),\n\t\t\tE('div', { 'class': 'right' }, [\n\t\t\t\tE('div', {\n\t\t\t\t\t'class': 'btn cbi-button-neutral',\n\t\t\t\t\t'click': ui.hideModal\n\t\t\t\t}, _('Cancel')),\n\t\t\t\t' ',\n\t\t\t\tE('div', {\n\t\t\t\t\t'class': 'btn cbi-button-positive',\n\t\t\t\t\t'click': clickToSaveConfig.bind(this, keylist, cstrs),\n\t\t\t\t\t'disabled': (L.hasViewPermission ? !L.hasViewPermission() : null) || null\n\t\t\t\t}, _('Save'))\n\t\t\t])\n\t\t];\n\t\tui.showModal(_('Configuration'), body);\n\t})\n}\n\nfunction loadCss(path) {\n\tvar head = document.head || document.getElementsByTagName('head')[0];\n\tvar link = E('link', {\n\t\t'rel': 'stylesheet',\n\t\t'href': path,\n\t\t'type': 'text/css'\n\t});\n\n\thead.appendChild(link);\n}\n\nfunction parseDatabase(raw, hosts, showZero, hideMACs) {\n\tvar values = [],\n\t    totals = [0, 0, 0, 0, 0],\n\t    rows = raw.trim().split(/\\r?\\n|\\r/g),\n\t    rowIndex = [1, 0, 3, 4, 5, 6, 7, 8, 9, 0];\n\n\trows.shift();\n\n\tfor (var i = 0; i < rows.length; i++) {\n\t\tvar row = rows[i].split(',');\n\t\tif ((!showZero && row[7] == 0) || hideMACs.indexOf(row[0]) >= 0) continue;\n\n\t\tfor (var j = 0; j < totals.length; j++) {\n\t\t\ttotals[j] += parseInt(row[3 + j]);\n\t\t}\n\n\t\tvar newRow = rowIndex.map(function(i) { return row[i] });\n\t\tif (newRow[1].toLowerCase() in hosts) {\n\t\t\tnewRow[9] = hosts[newRow[1].toLowerCase()];\n\t\t}\n\t\tvalues.push(newRow);\n\t}\n\n\treturn [values, totals];\n}\n\nfunction parseDefaultSettings(file) {\n\tvar defaultColumns = ['thClient', 'thDownload', 'thUpload', 'thTotalDown', 'thTotalUp', 'thTotal'],\n\t    keylist = ['protocol', 'interval', 'showColumns', 'showZero', 'useBits', 'useMultiple', 'useDSL', 'upstream', 'downstream', 'hideMACs'],\n\t    valuelist = ['ipv4', '5', defaultColumns, true, false, '1000', false, '100', '100', []];\n\n\treturn fs.read_direct(file, 'json').then(function(oldSettings) {\n\t\tvar settings = {};\n\t\tfor (var i = 0; i < keylist.length; i++) {\n\t\t\tif (!(keylist[i] in oldSettings))\n\t\t\t\tsettings[keylist[i]] = valuelist[i];\n\t\t\telse\n\t\t\t\tsettings[keylist[i]] = oldSettings[keylist[i]];\n\t\t}\n\n\t\tif (settings.useDSL) {\n\t\t\treturn getDSLBandwidth().then(function(dsl) {\n\t\t\t\tfor (var s in dsl)\n\t\t\t\t\tsettings[s] = dsl[s];\n\t\t\t\treturn settings;\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\treturn settings;\n\t\t}\n\t})\n\t.catch(function() { return {} });\n}\n\nfunction progressbar(query, v, m, useBits, useMultiple) {\n\t// v = B/s, m = Mb/s\n\tvar pg = $(query),\n\t    vn = (v * 8) || 0,\n\t    mn = (m || 100) * Math.pow(1000, 2),\n\t    fv = formatSpeed(v, useBits, useMultiple),\n\t    pc = '%.2f'.format((100 / mn) * vn),\n\t    wt = Math.floor(pc > 100 ? 100 : pc),\n\t    bgc = (pc >= 95 ? 'red' : (pc >= 80 ? 'darkorange' : (pc >= 60 ? 'yellow' : 'lime')));\n\tif (pg) {\n\t\tpg.firstElementChild.style.width = wt + '%';\n\t\tpg.firstElementChild.style.background = bgc;\n\t\tpg.setAttribute('title', '%s (%f%%)'.format(fv, pc));\n\t}\n}\n\nfunction setupThisDOM(settings, table) {\n\tdocument.addEventListener('poll-stop', function() {\n\t\t$('selectInterval').value = -1;\n\t});\n\n\tdocument.addEventListener('poll-start', function() {\n\t\t$('selectInterval').value = settings.interval;\n\t});\n\n\ttable.querySelectorAll('.th').forEach(function(e) {\n\t\tif (e) {\n\t\t\te.addEventListener('click', function (ev) {\n\t\t\t\tsetSortedColumn(ev.target);\n\t\t\t\tdisplayTable(table, settings);\n\t\t\t});\n\n\t\t\tif (settings.showColumns.indexOf(e.id) >= 0)\n\t\t\t\te.classList.remove('hide');\n\t\t\telse\n\t\t\t\te.classList.add('hide');\n\n\t\t}\n\t});\n}\n\nfunction renameFile(str, tag) {\n\tvar n = str.lastIndexOf('/'), fn = n > -1 ? str.slice(n + 1) : str, dir = n > -1 ? str.slice(0, n + 1) : '';\n\tvar n = fn.lastIndexOf('.'), bn = n > -1 ? fn.slice(0, n) : fn;\n\tvar n = fn.lastIndexOf('.'), en = n > -1 ? fn.slice(n + 1) : '';\n\treturn dir + bn + '.' + tag + (en ? '.' + en : '');\n}\n\nfunction resolveCustomizedHostName() {\n\treturn fs.stat(hostNameFile).then(function() {\n\t\treturn fs.read_direct(hostNameFile).then(function(raw) {\n\t\t\tvar arr = raw.trim().split(/\\r?\\n/), hosts = {}, row;\n\t\t\tfor (var i = 0; i < arr.length; i++) {\n\t\t\t\trow = arr[i].split(',');\n\t\t\t\tif (row.length == 2 && row[0])\n\t\t\t\t\thosts[row[0].toLowerCase()] = row[1];\n\t\t\t}\n\t\t\treturn hosts;\n\t\t})\n\t})\n\t.catch(function() { return []; });\n}\n\nfunction resolveHostNameByMACAddr() {\n\treturn Promise.all([\n\t\tresolveCustomizedHostName(),\n\t\tcallLuciDHCPLeases()\n\t]).then(function(res) {\n\t\tvar hosts = res[0];\n\t\tfor (var key in res[1]) {\n\t\t\tvar leases = Array.isArray(res[1][key]) ? res[1][key] : [];\n\t\t\tfor (var i = 0; i < leases.length; i++) {\n\t\t\t\tif(leases[i].macaddr) {\n\t\t\t\t\tvar macaddr = leases[i].macaddr.toLowerCase();\n\t\t\t\t\tif (!(macaddr in hosts) && Boolean(leases[i].hostname))\n\t\t\t\t\t\thosts[macaddr] = leases[i].hostname;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn hosts;\n\t});\n}\n\nfunction setSortedColumn(sorting) {\n\tvar sorted = document.querySelector('.th.sorted') || $('thTotal');\n\n\tif (sorting.isSameNode(sorted)) {\n\t\tsorting.classList.toggle('ascent');\n\t}\n\telse {\n\t\tsorting.classList.add('sorted');\n\t\tsorted.classList.remove('sorted', 'ascent');\n\t}\n}\n\nfunction setUpdateMessage(e, sec) {\n\te.innerHTML = sec < 0 ? '' : _('Updating again in %s second(s).').format('<b>' + sec + '</b>');\n}\n\nfunction sortTable(col, IPVer, flag, x, y) {\n\tvar byCol = x[col] == y[col] ? 1 : col;\n\tvar a = x[byCol], b = y[byCol];\n\n\tif (!IPVer || byCol != 0) {\n\t\tif (!(a.match(/\\D/g) || b.match(/\\D/g)))\n\t\t\ta = parseInt(a), b = parseInt(b);\n\t}\n\telse {\n\t\tIPVer == 'ipv4'\n\t\t? (a = validation.parseIPv4(a) || [0, 0, 0, 0], b = validation.parseIPv4(b) || [0, 0, 0, 0])\n\t\t: (a = validation.parseIPv6(a) || [0, 0, 0, 0, 0, 0, 0, 0], b = validation.parseIPv6(b) || [0, 0, 0, 0, 0, 0, 0, 0]);\n\t}\n\n\tif (Array.isArray(a) && Array.isArray(b)) {\n\t\tfor (var i = 0; i < a.length; i++) {\n\t\t\tif (a[i] != b[i]) {\n\t\t\t\treturn (b[i] - a[i]) * flag;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treturn a == b ? 0 : (a < b ? 1 : -1) * flag;\n}\n\nfunction updateData(settings, table, updated, updating, once) {\n\tvar tick = poll.tick,\n\t    interval = settings.interval,\n\t    sec = (interval - tick % interval) % interval;\n\tif (!sec || once) {\n\t\tcallGetDatabasePath()\n\t\t.then(function(res) {\n\t\t\tvar params = settings.protocol == 'ipv4' ? '-4' : '-6';\n\t\t\treturn fs.exec_direct('/usr/sbin/wrtbwmon', [params, '-f', res.file_4])\n\t\t})\n\t\t.then(function() {\n\t\t\treturn Promise.all([\n\t\t\t\tcallGetDatabaseRaw(settings.protocol),\n\t\t\t\tresolveHostNameByMACAddr()\n\t\t\t]);\n\t\t})\n\t\t.then(function(res) {\n\t\t\t//console.time('start');\n\t\t\tcachedData = parseDatabase(res[0].data || '', res[1], settings.showZero, settings.hideMACs);\n\t\t\tdisplayTable(table, settings);\n\t\t\tupdated.textContent = _('Last updated at %s.').format(formatDate(new Date(document.lastModified)));\n\t\t\t//console.timeEnd('start');\n\t\t});\n\t}\n\n\tsetUpdateMessage(updating, sec);\n\tif (!sec)\n\t\tsetTimeout(setUpdateMessage.bind(this, updating, interval), 100);\n}\n\nfunction updateTable(tb, values, placeholder, settings) {\n\tvar fragment = document.createDocumentFragment(), nodeLen = tb.childElementCount - 2;\n\tvar formData = values[0], tbTitle = tb.firstElementChild, newNode, childTD;\n\n\t// Update the table data.\n\tfor (var i = 0; i < formData.length; i++) {\n\t\tif (i < nodeLen) {\n\t\t\tnewNode = tbTitle.nextElementSibling;\n\t\t}\n\t\telse {\n\t\t\tif (nodeLen > 0) {\n\t\t\t\tnewNode = fragment.firstChild.cloneNode(true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnewNode = document.createElement('tr');\n\t\t\t\tchildTD = document.createElement('td');\n\t\t\t\tfor (var j = 0; j < tbTitle.children.length; j++) {\n\t\t\t\t\tchildTD.className = 'td' + (settings.showColumns.indexOf(tbTitle.children[j].id) >= 0 ? '' : ' hide');\n\t\t\t\t\tchildTD.setAttribute('data-title', tbTitle.children[j].textContent);\n\t\t\t\t\tnewNode.appendChild(childTD.cloneNode(true));\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewNode.className = 'tr cbi-rowstyle-%d'.format(i % 2 ? 2 : 1);\n\t\t}\n\n\t\tchildTD = newNode.firstElementChild;\n\t\tchildTD.title = formData[i].slice(-1);\n\t\tfor (var j = 0; j < tbTitle.childElementCount; j++, childTD = childTD.nextElementSibling) {\n\t\t\tswitch (j) {\n\t\t\t\tcase 2:\n\t\t\t\tcase 3:\n\t\t\t\t\tchildTD.textContent = formatSpeed(formData[i][j], settings.useBits, settings.useMultiple);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\tcase 5:\n\t\t\t\tcase 6:\n\t\t\t\t\tchildTD.textContent = formatSize(formData[i][j], settings.useBits, settings.useMultiple);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\tcase 8:\n\t\t\t\t\tchildTD.textContent = formatDate(new Date(formData[i][j] * 1000));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tchildTD.textContent = formData[i][j];\n\t\t\t}\n\t\t}\n\t\tfragment.appendChild(newNode);\n\t}\n\n\t// Remove the table data which has been deleted from the database.\n\twhile (tb.childElementCount > 1) {\n\t\ttb.removeChild(tbTitle.nextElementSibling);\n\t}\n\n\t//Append the totals or placeholder row.\n\tif (formData.length == 0) {\n\t\tnewNode = document.createElement('tr');\n\t\tnewNode.className = 'tr placeholder';\n\t\tchildTD = document.createElement('td');\n\t\tchildTD.className = 'td';\n\t\tchildTD.innerHTML = placeholder;\n\t\tnewNode.appendChild(childTD);\n\t}\n\telse{\n\t\tnewNode = fragment.firstChild.cloneNode(true);\n\t\tnewNode.className = 'tr table-totals';\n\n\t\tnewNode.children[0].textContent = _('TOTAL') + (settings.showColumns.indexOf('thMAC') >= 0 ? '' : ': ' + formData.length);\n\t\tnewNode.children[1].textContent = formData.length + ' ' + _('Clients');\n\n\t\tfor (var j = 0; j < tbTitle.childElementCount; j++) {\n\t\t\tswitch(j) {\n\t\t\t\tcase 0:\n\t\t\t\tcase 1:\n\t\t\t\t\tnewNode.children[j].removeAttribute('title');\n\t\t\t\t\tnewNode.children[j].style.fontWeight = 'bold';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\tcase 3:\n\t\t\t\t\tnewNode.children[j].textContent = formatSpeed(values[1][j - 2], settings.useBits, settings.useMultiple);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\tcase 5:\n\t\t\t\tcase 6:\n\t\t\t\t\tnewNode.children[j].textContent = formatSize(values[1][j - 2], settings.useBits, settings.useMultiple);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tnewNode.children[j].textContent = '';\n\t\t\t\t\tnewNode.children[j].removeAttribute('data-title');\n\t\t\t}\n\t\t}\n\t}\n\n\tfragment.appendChild(newNode);\n\ttb.appendChild(fragment);\n}\n\nfunction initOption(options, selected) {\n\tvar res = [], attr = {};\n\tfor (var idx in options) {\n\t\tattr.value = idx;\n\t\tattr.selected = idx == selected ? '' : null;\n\t\tres.push(E('option', attr, options[idx]));\n\t}\n\treturn res;\n}\n\nreturn view.extend({\n\tload: function() {\n\t\treturn Promise.all([\n\t\t\tparseDefaultSettings(luciConfig),\n\t\t\tloadCss(L.resource('view/wrtbwmon/wrtbwmon.css'))\n\t\t]);\n\t},\n\n\trender: function(data) {\n\t\tvar settings = data[0],\n\t\t    labelUpdated = E('label'),\n\t\t    labelUpdating = E('label'),\n\t\t    table = E('table', { 'class': 'table', 'id': 'traffic' }, [\n\t\t\t\t\tE('tr', { 'class': 'tr table-titles' }, [\n\t\t\t\t\t\tE('th', { 'class': 'th', 'id': 'thClient' }, _('Clients')),\n\t\t\t\t\t\tE('th', { 'class': 'th hide', 'id': 'thMAC' }, _('MAC')),\n\t\t\t\t\t\tE('th', { 'class': 'th', 'id': 'thDownload' }, _('Download')),\n\t\t\t\t\t\tE('th', { 'class': 'th', 'id': 'thUpload' }, _('Upload')),\n\t\t\t\t\t\tE('th', { 'class': 'th', 'id': 'thTotalDown' }, _('Total Down')),\n\t\t\t\t\t\tE('th', { 'class': 'th', 'id': 'thTotalUp' }, _('Total Up')),\n\t\t\t\t\t\tE('th', { 'class': 'th sorted', 'id': 'thTotal' }, _('Total')),\n\t\t\t\t\t\tE('th', { 'class': 'th hide', 'id': 'thFirstSeen' }, _('First Seen')),\n\t\t\t\t\t\tE('th', { 'class': 'th hide', 'id': 'thLastSeen' }, _('Last Seen'))\n\t\t\t\t\t]),\n\t\t\t\t\tE('tr', {'class': 'tr placeholder'}, [\n\t\t\t\t\t\tE('td', { 'class': 'td' }, E('em', {}, _('Collecting data...')))\n\t\t\t\t\t])\n\t\t\t\t]);\n\n\t\tpoll.add(updateData.bind(this, settings, table, labelUpdated, labelUpdating, false), 1);\n\t\tsetupThisDOM(settings, table);\n\t\treturn E('div', { 'class': 'cbi-map' }, [\n\t\t\tE('h2', {}, _('Usage - Details')),\n\t\t\tE('div', { 'class': 'cbi-section' }, [\n\t\t\t\tE('div', { 'id': 'control_panel' }, [\n\t\t\t\t\tE('div', {}, [\n\t\t\t\t\t\tE('label', {}, _('Protocol:')),\n\t\t\t\t\t\tE('select', {\n\t\t\t\t\t\t\t'id': 'selectProtocol',\n\t\t\t\t\t\t\t'change': clickToSelectProtocol.bind(this, settings, table, labelUpdated, labelUpdating)\n\t\t\t\t\t\t\t}, initOption({\n\t\t\t\t\t\t\t\t'ipv4': 'ipv4',\n\t\t\t\t\t\t\t\t'ipv6': 'ipv6'\n\t\t\t\t\t\t\t\t}, settings.protocol))\n\t\t\t\t\t]),\n\t\t\t\t\tE('div', {}, [\n\t\t\t\t\t\tE('button', {\n\t\t\t\t\t\t\t'class': 'btn cbi-button cbi-button-reset important',\n\t\t\t\t\t\t\t'id': 'resetDatabase',\n\t\t\t\t\t\t\t'click': clickToResetDatabase.bind(this, settings, table, labelUpdated, labelUpdating)\n\t\t\t\t\t\t}, _('Reset Database')),\n\t\t\t\t\t\t' ',\n\t\t\t\t\t\tE('button', {\n\t\t\t\t\t\t\t'class': 'btn cbi-button cbi-button-neutral',\n\t\t\t\t\t\t\t'click': handleConfig\n\t\t\t\t\t\t}, _('Configure Options'))\n\t\t\t\t\t])\n\t\t\t\t]),\n\t\t\t\tE('div', {}, [\n\t\t\t\t\tE('div', {}, [ labelUpdated, labelUpdating ]),\n\t\t\t\t\tE('div', {}, [\n\t\t\t\t\t\tE('label', { 'for': 'selectInterval' }, _('Auto update every:')),\n\t\t\t\t\t\tE('select', {\n\t\t\t\t\t\t\t'id': 'selectInterval',\n\t\t\t\t\t\t\t'change': clickToSelectInterval.bind(this, settings, labelUpdating)\n\t\t\t\t\t\t\t}, initOption({\n\t\t\t\t\t\t\t\t'-1': _('Disabled'),\n\t\t\t\t\t\t\t\t'2': _('2 seconds'),\n\t\t\t\t\t\t\t\t'5': _('5 seconds'),\n\t\t\t\t\t\t\t\t'10': _('10 seconds'),\n\t\t\t\t\t\t\t\t'30': _('30 seconds')\n\t\t\t\t\t\t\t\t}, settings.interval))\n\t\t\t\t\t])\n\t\t\t\t]),\n\t\t\t\tE('div', { 'id': 'progressbar_panel' }, [\n\t\t\t\t\tE('div', {}, [\n\t\t\t\t\t\tE('label', {},  _('Downstream:')),\n\t\t\t\t\t\tE('div', {\n\t\t\t\t\t\t\t'id': 'downstream',\n\t\t\t\t\t\t\t'class': 'cbi-progressbar',\n\t\t\t\t\t\t\t'title': '-'\n\t\t\t\t\t\t\t}, E('div')\n\t\t\t\t\t\t)\n\t\t\t\t\t]),\n\t\t\t\t\tE('div', {}, [\n\t\t\t\t\t\tE('label', {}, _('Upstream:')),\n\t\t\t\t\t\tE('div', {\n\t\t\t\t\t\t\t'id': 'upstream',\n\t\t\t\t\t\t\t'class': 'cbi-progressbar',\n\t\t\t\t\t\t\t'title': '-'\n\t\t\t\t\t\t\t}, E('div')\n\t\t\t\t\t\t)\n\t\t\t\t\t]),\n\t\t\t\t]),\n\t\t\t\ttable\n\t\t\t])\n\t\t]);\n\t},\n\n\thandleSaveApply: null,\n\thandleSave: null,\n\thandleReset: null\n});\n"
  },
  {
    "path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css",
    "content": ".th.sorted::after {\n\tcontent: '\\25bc';\n}\n.th.sorted.ascent::after {\n\tcontent: '\\25b2';\n}\n.hide {\n\tdisplay: none !important;\n}\n#control_panel {\n\tdisplay: flex;\n\tmargin-bottom: 1rem;\n\tpadding: .5rem;\n\tline-height: 2rem;\n}\n#control_panel > :nth-child(1) {\n\tdisplay: inline-block;\n\tflex: 1 1 50%;\n}\n#control_panel > :nth-child(2) {\n\tflex: 1 1 50%;\n\ttext-align: right;\n}\n#control_panel > * > * {\n\tvertical-align: middle;\n}\n#control_panel > div > label {\n\tmargin-right: .5rem;\n}\ndiv > label + select {\n\tmin-width: unset;\n\twidth: auto;\n}\n#control_panel + div {\n\tdisplay: flex;\n\tmargin-bottom: .5rem;\n}\n#control_panel + div > div:nth-of-type(1) {\n\tflex: 1 1 65%;\n}\n#control_panel + div > div:nth-of-type(2) {\n\tflex: 1 1 35%;\n\ttext-align: right;\n}\n#thClient {\n\twidth: 17%;\n}\n#thMAC {\n\twidth: 10%;\n}\n#thDownload, #thUpload {\n\twidth: 8%;\n}\n#thTotalDown, #thTotalUp, #thTotal {\n\twidth: 9%;\n}\n#thFirstSeen, #thLastSeen {\n\twidth: 15%;\n}\n.tr.table-totals {\n\tfont-weight: bold;\n}\n#traffic .tr:not(.table-totals):not(.placeholder) > .td:not(.th):first-child::before {\n\tcontent: attr(title)'\\a';\n\twhite-space: pre-line;\n}\n"
  },
  {
    "path": "luci-app-wrtbwmon/po/templates/wrtbwmon.pot",
    "content": "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611\nmsgid \"10 seconds\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609\nmsgid \"2 seconds\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612\nmsgid \"30 seconds\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610\nmsgid \"5 seconds\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603\nmsgid \"Auto update every:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556\nmsgid \"Clients\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567\nmsgid \"Collecting data...\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597\nmsgid \"Configure Options\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195\nmsgid \"Configure the default values for luci-app-wrtbwmon.\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"Database path\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178\nmsgid \"Default Columns\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"Default Protocol\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\nmsgid \"Default Refresh Interval\"\nmsgstr \"\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16\nmsgid \"Details\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558\nmsgid \"Download\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184\nmsgid \"Downstream Bandwidth\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618\nmsgid \"Downstream:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18\nmsgid \"Each line must have the following format:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563\nmsgid \"First Seen\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18\nmsgid \"General settings\"\nmsgstr \"\"\n\n#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3\nmsgid \"Grant access to LuCI app wrtbwmon\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185\nmsgid \"Hide MAC Addresses\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"IEC - 1024\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21\nmsgid \"Keep running in the background\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564\nmsgid \"Last Seen\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425\nmsgid \"Last updated at %s.\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168\nmsgid \"Loading configuration data...\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557\nmsgid \"MAC\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"Multiple of Unit\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578\nmsgid \"Protocol:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592\nmsgid \"Reset Database\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"SI - 1000\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207\nmsgid \"Save\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76\nmsgid \"Saving configuration data...\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179\nmsgid \"Show Zeros\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502\nmsgid \"TOTAL\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"\"\n\"This box is used to select the Database path, which is /tmp/usage.db by \"\n\"default.\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60\nmsgid \"This will delete the database file. Are you sure?\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562\nmsgid \"Total\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560\nmsgid \"Total Down\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561\nmsgid \"Total Up\"\nmsgstr \"\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3\nmsgid \"Traffic Status\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180\nmsgid \"Transfer Speed in Bits\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9\nmsgid \"Unable to load the customized hostname file:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81\nmsgid \"Unable to save %s: %s\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376\nmsgid \"Updating again in %s second(s).\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183\nmsgid \"Upstream Bandwidth\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627\nmsgid \"Upstream:\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16\nmsgid \"Usage - Configuration\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16\nmsgid \"Usage - Custom User File\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574\nmsgid \"Usage - Details\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182\nmsgid \"Use DSL Bandwidth\"\nmsgstr \"\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34\nmsgid \"User file\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv4\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv6\"\nmsgstr \"\"\n"
  },
  {
    "path": "luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po",
    "content": "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\\n\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611\nmsgid \"10 seconds\"\nmsgstr \"10秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609\nmsgid \"2 seconds\"\nmsgstr \"2秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612\nmsgid \"30 seconds\"\nmsgstr \"30秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610\nmsgid \"5 seconds\"\nmsgstr \"5秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603\nmsgid \"Auto update every:\"\nmsgstr \"自动刷新：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201\nmsgid \"Cancel\"\nmsgstr \"取消\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556\nmsgid \"Clients\"\nmsgstr \"客户端\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567\nmsgid \"Collecting data...\"\nmsgstr \"收集数据中...\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597\nmsgid \"Configure Options\"\nmsgstr \"配置选项\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195\nmsgid \"Configure the default values for luci-app-wrtbwmon.\"\nmsgstr \"配置luci-app-wrtbwmon的默认值。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"Database path\"\nmsgstr \"数据路径\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178\nmsgid \"Default Columns\"\nmsgstr \"默认显示的列\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"Default Protocol\"\nmsgstr \"默认协议\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\nmsgid \"Default Refresh Interval\"\nmsgstr \"默认刷新间隔\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16\nmsgid \"Details\"\nmsgstr \"流量信息\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608\nmsgid \"Disabled\"\nmsgstr \"禁用\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558\nmsgid \"Download\"\nmsgstr \"下载\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184\nmsgid \"Downstream Bandwidth\"\nmsgstr \"下行带宽\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618\nmsgid \"Downstream:\"\nmsgstr \"下行：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18\nmsgid \"Each line must have the following format:\"\nmsgstr \"每行需要满足以下格式：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563\nmsgid \"First Seen\"\nmsgstr \"初次记录\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18\nmsgid \"General settings\"\nmsgstr \"通用设置\"\n\n#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3\nmsgid \"Grant access to LuCI app wrtbwmon\"\nmsgstr \"授予访问LuCI应用程序wrtbwmon的权限\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185\nmsgid \"Hide MAC Addresses\"\nmsgstr \"隐藏MAC地址\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"IEC - 1024\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21\nmsgid \"Keep running in the background\"\nmsgstr \"保持后台运行\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564\nmsgid \"Last Seen\"\nmsgstr \"最后记录\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425\nmsgid \"Last updated at %s.\"\nmsgstr \"最后更新于%s。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168\nmsgid \"Loading configuration data...\"\nmsgstr \"载入配置数据...\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557\nmsgid \"MAC\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"Multiple of Unit\"\nmsgstr \"单位之间倍数\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578\nmsgid \"Protocol:\"\nmsgstr \"协议：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592\nmsgid \"Reset Database\"\nmsgstr \"重置\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"SI - 1000\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207\nmsgid \"Save\"\nmsgstr \"保存\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76\nmsgid \"Saving configuration data...\"\nmsgstr \"保存配置数据...\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179\nmsgid \"Show Zeros\"\nmsgstr \"显示0流量\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502\nmsgid \"TOTAL\"\nmsgstr \"总共\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"\"\n\"This box is used to select the Database path, which is /tmp/usage.db by \"\n\"default.\"\nmsgstr \"该选项用于选择数据存储路径，默认路径为/tmp/usage.db。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60\nmsgid \"This will delete the database file. Are you sure?\"\nmsgstr \"该操作将删除数据统计文件，确定执行该操作？\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562\nmsgid \"Total\"\nmsgstr \"总计\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560\nmsgid \"Total Down\"\nmsgstr \"总下载\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561\nmsgid \"Total Up\"\nmsgstr \"总上传\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3\nmsgid \"Traffic Status\"\nmsgstr \"流量监控\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180\nmsgid \"Transfer Speed in Bits\"\nmsgstr \"以bits显示传输速度\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9\nmsgid \"Unable to load the customized hostname file:\"\nmsgstr \"不能载入自定义用户名文件：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81\nmsgid \"Unable to save %s: %s\"\nmsgstr \"不能保存%s: %s\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376\nmsgid \"Updating again in %s second(s).\"\nmsgstr \"下次更新将于%s秒之后。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559\nmsgid \"Upload\"\nmsgstr \"上传\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183\nmsgid \"Upstream Bandwidth\"\nmsgstr \"上传带宽\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627\nmsgid \"Upstream:\"\nmsgstr \"上行：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16\nmsgid \"Usage - Configuration\"\nmsgstr \"文件设置\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16\nmsgid \"Usage - Custom User File\"\nmsgstr \"用户文件配置\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574\nmsgid \"Usage - Details\"\nmsgstr \"流量详情\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182\nmsgid \"Use DSL Bandwidth\"\nmsgstr \"使用DSL带宽\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34\nmsgid \"User file\"\nmsgstr \"用户文件\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv4\"\nmsgstr \"\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv6\"\nmsgstr \"\"\n\n#~ msgid \"Show More Columns:\"\n#~ msgstr \"显示更多列：\"\n\n#~ msgid \"This will revert the changes. Are you sure?\"\n#~ msgstr \"更改将会重置，确定吗？\"\n"
  },
  {
    "path": "luci-app-wrtbwmon/po/zh_Hant/wrtbwmon.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"POT-Creation-Date: \\n\"\n\"PO-Revision-Date: 2023-04-26 18:31+0800\\n\"\n\"Last-Translator: Victor Tseng (palatis@gmail.com)\\n\"\n\"Language-Team: \\n\"\n\"Language: zh_TW\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Poedit 3.2.2\\n\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611\nmsgid \"10 seconds\"\nmsgstr \"10 秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609\nmsgid \"2 seconds\"\nmsgstr \"2 秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612\nmsgid \"30 seconds\"\nmsgstr \"30 秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610\nmsgid \"5 seconds\"\nmsgstr \"5 秒\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603\nmsgid \"Auto update every:\"\nmsgstr \"重新整理間隔：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201\nmsgid \"Cancel\"\nmsgstr \"取消\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556\nmsgid \"Clients\"\nmsgstr \"用戶端\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567\nmsgid \"Collecting data...\"\nmsgstr \"蒐集資料中…\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25\nmsgid \"Configuration\"\nmsgstr \"設定\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597\nmsgid \"Configure Options\"\nmsgstr \"設定選項\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195\nmsgid \"Configure the default values for luci-app-wrtbwmon.\"\nmsgstr \"設定 luci-app-wrtbwmon 的預設值。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"Database path\"\nmsgstr \"資料庫路徑\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178\nmsgid \"Default Columns\"\nmsgstr \"預設顯示的欄位\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"Default Protocol\"\nmsgstr \"預設協定\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\nmsgid \"Default Refresh Interval\"\nmsgstr \"預設重新整理間隔\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16\nmsgid \"Details\"\nmsgstr \"詳細\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608\nmsgid \"Disabled\"\nmsgstr \"已停用\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558\nmsgid \"Download\"\nmsgstr \"下載\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184\nmsgid \"Downstream Bandwidth\"\nmsgstr \"下行頻寬\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618\nmsgid \"Downstream:\"\nmsgstr \"下行：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18\nmsgid \"Each line must have the following format:\"\nmsgstr \"每一行皆必須使用以下格式：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563\nmsgid \"First Seen\"\nmsgstr \"初次發現\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18\nmsgid \"General settings\"\nmsgstr \"通用設定\"\n\n#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3\nmsgid \"Grant access to LuCI app wrtbwmon\"\nmsgstr \"授權給 LuCI app wrtbwmon\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185\nmsgid \"Hide MAC Addresses\"\nmsgstr \"隱藏 MAC 地址\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"IEC - 1024\"\nmsgstr \"IEC - 1024\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21\nmsgid \"Keep running in the background\"\nmsgstr \"保持背景執行\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564\nmsgid \"Last Seen\"\nmsgstr \"最後發現\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425\nmsgid \"Last updated at %s.\"\nmsgstr \"最後於 %s 發現。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168\nmsgid \"Loading configuration data...\"\nmsgstr \"載入設定資料…\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557\nmsgid \"MAC\"\nmsgstr \"MAC\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"Multiple of Unit\"\nmsgstr \"速率計量單位格式\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578\nmsgid \"Protocol:\"\nmsgstr \"協定：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592\nmsgid \"Reset Database\"\nmsgstr \"重置資料庫\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181\nmsgid \"SI - 1000\"\nmsgstr \"SI - 1000\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207\nmsgid \"Save\"\nmsgstr \"儲存\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76\nmsgid \"Saving configuration data...\"\nmsgstr \"正在保存設定資料…\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179\nmsgid \"Show Zeros\"\nmsgstr \"顯示無流量的紀錄\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502\nmsgid \"TOTAL\"\nmsgstr \"總計\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24\nmsgid \"\"\n\"This box is used to select the Database path, which is /tmp/usage.db by \"\n\"default.\"\nmsgstr \"選擇資料庫路徑，預設為 /tmp/usage.db。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60\nmsgid \"This will delete the database file. Are you sure?\"\nmsgstr \"這會刪除目前的資料庫檔案，您確定嗎？\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562\nmsgid \"Total\"\nmsgstr \"總頻寬\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560\nmsgid \"Total Down\"\nmsgstr \"總下載\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561\nmsgid \"Total Up\"\nmsgstr \"總上傳\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3\nmsgid \"Traffic Status\"\nmsgstr \"頻寬使用狀況\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180\nmsgid \"Transfer Speed in Bits\"\nmsgstr \"以位元表示傳輸速率\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9\nmsgid \"Unable to load the customized hostname file:\"\nmsgstr \"無法載入自訂的主機名稱檔案：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81\nmsgid \"Unable to save %s: %s\"\nmsgstr \"無法保存 %s：%s\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376\nmsgid \"Updating again in %s second(s).\"\nmsgstr \"於 %s 秒後更新。\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559\nmsgid \"Upload\"\nmsgstr \"上傳\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183\nmsgid \"Upstream Bandwidth\"\nmsgstr \"上行頻寬\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627\nmsgid \"Upstream:\"\nmsgstr \"上行：\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16\nmsgid \"Usage - Configuration\"\nmsgstr \"頻寬使用狀況 - 設定\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16\nmsgid \"Usage - Custom User File\"\nmsgstr \"頻寬使用狀況 - 自訂主機名稱列表\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574\nmsgid \"Usage - Details\"\nmsgstr \"頻寬使用狀況 - 詳細資料\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182\nmsgid \"Use DSL Bandwidth\"\nmsgstr \"使用 DSL 頻寬\"\n\n#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34\nmsgid \"User file\"\nmsgstr \"使用者檔案\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv4\"\nmsgstr \"ipv4\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176\nmsgid \"ipv6\"\nmsgstr \"ipv6\"\n\n#~ msgid \"Default More Columns\"\n#~ msgstr \"預設顯示更多欄位\"\n\n#~ msgid \"Show More Columns:\"\n#~ msgstr \"顯示更多欄位：\"\n"
  },
  {
    "path": "luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf",
    "content": "{\n\t\"protocol\": \"ipv4\",\n\t\"interval\": \"5\",\n\t\"showMore\": false,\n\t\"showZero\": true,\n\t\"useBits\": false,\n\t\"useMultiple\": \"1000\",\n\t\"useDSL\": false,\n\t\"upstream\": \"100\",\n\t\"downstream\": \"100\",\n\t\"hideMACs\": []\n}\n"
  },
  {
    "path": "luci-app-wrtbwmon/root/usr/libexec/rpcd/luci.wrtbwmon",
    "content": "#!/bin/sh\n\n. \"$IPKG_INSTROOT/usr/share/libubox/jshn.sh\"\n\nrenamefile() {\n\tlocal base=$(basename -- \"$1\")\n\tlocal ext=$([ -z \"${base/*.*/}\"  ] && echo \".${base##*.}\" || echo '')\n\tlocal base=\"${base%.*}\"\n\techo \"$(dirname $1)/${base}$2$ext\" && return\n}\n\n_get_db_path() {\n\tlocal db db_4 db_6 db_46\n\tdb=\"$(uci -q get wrtbwmon.general.path)\"\n\tdb_4=$(renamefile \"${db:-/tmp/usage.db}\" \"\")\n\tdb_6=$(renamefile \"${db:-/tmp/usage.db}\" \".6\")\n\tdb_46=$(renamefile \"${db:-/tmp/usage.db}\" \".46\")\n\tjson_init\n\tjson_add_string file_4 \"$db_4\"\n\tjson_add_string file_6 \"$db_6\"\n\tjson_add_string file_46 \"$db_46\"\n\tjson_dump\n\tjson_cleanup\n}\n\n_get_db_raw() {\n\tjson_init\n\tjson_add_string file \"$1\"\n\tjson_add_string data \"$(cat \"$1\")\"\n\tjson_dump\n\tjson_cleanup\n}\n\n_remove_db() {\n\tlocal result\n\trm \"$1\" && result=1 || result=0\n\tjson_init\n\tjson_add_boolean result \"$result\"\n\tjson_dump\n\tjson_cleanup\n}\n\n_change_db_path() {\n\tjson_init\n\tjson_load \"$(_get_db_path)\"\n\tjson_get_var db_4 'file_4'\n\tjson_get_var db_6 'file_6'\n\tjson_get_var db_46 'file_46'\n\tjson_cleanup\n\tif [ \"$1\" = \"before\" ]; then\n\t\tmv \"$db_4\" /tmp/usage.db.tmp\n\t\tmv \"$db_6\" /tmp/usage.6.db.tmp\n\t\tmv \"$db_46\" /tmp/usage.46.db.tmp\n\telif [ \"$1\" = \"after\" ]; then\n\t\tmv -f /tmp/usage.db.tmp \"$db_4\"\n\t\tmv -f /tmp/usage.6.db.tmp \"$db_6\"\n\t\tmv -f /tmp/usage.46.db.tmp \"$db_46\"\n\tfi\n\tjson_init\n\tjson_add_boolean result $([ \"$?\" = 0 ] && echo 1 || echo 0 )\n\tjson_dump\n\tjson_cleanup\n}\n\ncase \"$1\" in\n\tlist)\n\t\tjson_init\n\t\tjson_add_object remove_db\n\t\tjson_add_string protocol \"protocol\"\n\t\tjson_close_object\n\t\tjson_add_object get_db_raw\n\t\tjson_add_string protocol \"protocol\"\n\t\tjson_close_object\n\t\tjson_add_object get_db_path\n\t\tjson_close_object\n\t\tjson_add_object change_db_path\n\t\tjson_add_string state \"state\"\n\t\tjson_close_object\n\t\tjson_dump\n\t\tjson_cleanup\n\t;;\n\tcall)\n\t\tcase \"$2\" in\n\t\t\tremove_db)\n\t\t\t\tread -r input\n\t\t\t\tjson_init\n\t\t\t\tjson_load \"$input\"\n\t\t\t\tjson_get_var protocol 'protocol'\n\t\t\t\tjson_cleanup\n\t\t\t\tjson_load \"$(_get_db_path)\"\n\t\t\t\tjson_get_var db_s $([ \"$protocol\" = \"ipv4\" ] && echo file_4 || echo file_6)\n\t\t\t\tjson_cleanup\n\t\t\t\t_remove_db \"$db_s\"\n\t\t\t;;\n\t\t\tget_db_raw)\n\t\t\t\tread -r input\n\t\t\t\tjson_init\n\t\t\t\tjson_load \"$input\"\n\t\t\t\tjson_get_var protocol 'protocol'\n\t\t\t\tjson_cleanup\n\t\t\t\tjson_load \"$(_get_db_path)\"\n\t\t\t\tjson_get_var db_s $([ \"$protocol\" = \"ipv4\" ] && echo file_4 || echo file_6)\n\t\t\t\tjson_cleanup\n\t\t\t\t_get_db_raw \"$db_s\"\n\t\t\t;;\n\t\t\tget_db_path)\n\t\t\t\tread -r input\n\t\t\t\tjson_init\n\t\t\t\tjson_load \"$input\"\n\t\t\t\tjson_get_var protocol 'protocol'\n\t\t\t\tjson_cleanup\n\t\t\t\t_get_db_path\n\t\t\t;;\n\t\t\tchange_db_path)\n\t\t\t\tread -r input\n\t\t\t\tjson_init\n\t\t\t\tjson_load \"$input\"\n\t\t\t\tjson_get_var state 'state'\n\t\t\t\tjson_cleanup\n\t\t\t\t_change_db_path \"$state\"\n\t\t\t;;\n\t\tesac\n\t;;\nesac\n"
  },
  {
    "path": "luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json",
    "content": "{\n\t\"admin/network/usage\": {\n\t\t\"title\": \"Traffic Status\",\n\t\t\"order\": 60,\n\t\t\"action\": {\n\t\t\t\"type\": \"alias\",\n\t\t\t\"path\": \"admin/network/usage/details\"\n\t\t},\n\t\t\"depends\": {\n\t\t\t\"acl\": [ \"luci-app-wrtbwmon\" ],\n\t\t\t\"uci\": { \"wrtbwmon\": true }\n\t\t}\n\t},\n\n\t\"admin/network/usage/details\": {\n\t\t\"title\": \"Details\",\n\t\t\"order\": 10,\n\t\t\"action\": {\n\t\t\t\"type\": \"view\",\n\t\t\t\"path\": \"wrtbwmon/details\"\n\t\t}\n\t},\n\n\t\"admin/network/usage/config\": {\n\t\t\"title\": \"Configuration\",\n\t\t\"order\": 20,\n\t\t\"action\": {\n\t\t\t\"type\": \"view\",\n\t\t\t\"path\": \"wrtbwmon/config\"\n\t\t}\n\t},\n\n\t\"admin/network/usage/custom\": {\n\t\t\"title\": \"User file\",\n\t\t\"order\": 30,\n\t\t\"action\": {\n\t\t\t\"type\": \"view\",\n\t\t\t\"path\": \"wrtbwmon/custom\"\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json",
    "content": "{\n\t\"luci-app-wrtbwmon\": {\n\t\t\"description\": \"Grant access to LuCI app wrtbwmon\",\n\t\t\"read\": {\n\t\t\t\"ubus\": {\n\t\t\t\t\"luci.wrtbwmon\": [\n\t\t\t\t\t\"get_db_raw\",\n\t\t\t\t\t\"get_db_path\"\n\t\t\t\t],\n\t\t\t\t\"luci-rpc\": [\n\t\t\t\t\t\"getDHCPLeases\",\n\t\t\t\t\t\"getDSLStatus\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"file\": {\n\t\t\t\t\"/etc/wrtbwmon.user\": [ \"read\" ],\n\t\t\t\t\"/usr/sbin/wrtbwmon\": [ \"exec\" ],\n\t\t\t\t\"/etc/luci-wrtbwmon.conf\": [ \"read\" ]\n\t\t\t},\n\t\t\t\"uci\": [ \"wrtbwmon\" ]\n\t\t},\n\t\t\"write\": {\n\t\t\t\"ubus\": {\n\t\t\t\t\"luci.wrtbwmon\": [\n\t\t\t\t\t\"remove_db\",\n\t\t\t\t\t\"change_db_path\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"file\": {\n\t\t\t\t\"/etc/luci-wrtbwmon.conf\": [ \"write\" ],\n\t\t\t\t\"/etc/wrtbwmon.user\": [ \"write\" ]\n\t\t\t},\n\t\t\t\"uci\": [ \"wrtbwmon\" ]\n\t\t}\n\t}\n}\n"
  }
]