├── .browserslistrc ├── .gitignore ├── .stylelintrc ├── LICENSE ├── README.md ├── buildscripts ├── bundle.sh ├── deploy.sh ├── env.example └── vars ├── package-lock.json ├── package.json ├── postcss.config.js ├── screenshots ├── desktop-firewall.png ├── desktop-overview.png ├── desktop-system.png ├── desktop-uci-changes.png ├── mobile-menu.png ├── mobile-overview.png ├── mobile-system.png └── mobile-uci-changes.png ├── src ├── luci-theme-tano │ ├── Makefile │ ├── htdocs │ │ └── luci-static │ │ │ ├── resources │ │ │ └── menu-tano.js │ │ │ └── tano │ │ │ ├── fa-bars.svg │ │ │ ├── favicon.png │ │ │ ├── fonts │ │ │ ├── Carlito-Bold.woff │ │ │ ├── Carlito-Bold.woff2 │ │ │ ├── Carlito-BoldItalic.woff │ │ │ ├── Carlito-BoldItalic.woff2 │ │ │ ├── Carlito-Italic.woff │ │ │ ├── Carlito-Italic.woff2 │ │ │ ├── Carlito.woff │ │ │ └── Carlito.woff2 │ │ │ └── logo.svg │ ├── luasrc │ │ └── view │ │ │ └── themes │ │ │ └── tano │ │ │ ├── .keep │ │ │ ├── footer.htm │ │ │ └── header.htm │ ├── po │ │ ├── ru │ │ │ └── tano.po │ │ └── templates │ │ │ └── tano.pot │ └── root │ │ └── etc │ │ └── uci-defaults │ │ └── luci-theme-tano ├── scripts │ └── index.js └── styles │ ├── alerts.scss │ ├── breadcrumbs.scss │ ├── buttons.scss │ ├── cbi.scss │ ├── changes.scss │ ├── dropdown.scss │ ├── filebrowser.scss │ ├── fonts.scss │ ├── index.scss │ ├── inputs.scss │ ├── labels.scss │ ├── layout.scss │ ├── luci-app-nlbwmon.scss │ ├── luci-app-opkg.scss │ ├── menu.scss │ ├── mixins.scss │ ├── modal.scss │ ├── network-widgets.scss │ ├── spinner.scss │ ├── tables.scss │ ├── tabs.scss │ ├── tooltip.scss │ └── variables.scss └── webpack.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 5% 2 | IE 10 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *.log 4 | *.po~ 5 | buildscripts/env 6 | .idea/ 7 | node_modules/ 8 | bundle/ 9 | dist/ 10 | tmp/ 11 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "stylelint-scss", 4 | "stylelint-order" 5 | ], 6 | "rules": { 7 | "scss/at-rule-no-unknown": true, 8 | "block-no-empty": true, 9 | "color-no-invalid-hex": true, 10 | "comment-no-empty": true, 11 | "declaration-block-no-duplicate-properties": [ 12 | true, 13 | { 14 | "ignore": [ 15 | "consecutive-duplicates-with-different-values" 16 | ] 17 | } 18 | ], 19 | "declaration-block-no-shorthand-property-overrides": true, 20 | "font-family-no-duplicate-names": true, 21 | "font-family-no-missing-generic-family-keyword": true, 22 | "function-calc-no-unspaced-operator": true, 23 | "function-linear-gradient-no-nonstandard-direction": true, 24 | "keyframe-declaration-no-important": true, 25 | "media-feature-name-no-unknown": true, 26 | "no-descending-specificity": true, 27 | "no-duplicate-at-import-rules": true, 28 | "no-duplicate-selectors": true, 29 | "no-empty-source": true, 30 | "no-extra-semicolons": true, 31 | "no-invalid-double-slash-comments": true, 32 | "order/properties-alphabetical-order": true, 33 | "property-no-unknown": true, 34 | "selector-pseudo-class-no-unknown": true, 35 | "selector-pseudo-element-no-unknown": true, 36 | "selector-type-no-unknown": true, 37 | "string-no-newline": true, 38 | "unit-no-unknown": true, 39 | "at-rule-empty-line-before": [ 40 | "always", 41 | { 42 | "except": [ 43 | "blockless-after-same-name-blockless", 44 | "first-nested" 45 | ], 46 | "ignore": [ 47 | "after-comment" 48 | ] 49 | } 50 | ], 51 | "at-rule-name-case": "lower", 52 | "at-rule-name-space-after": "always-single-line", 53 | "at-rule-semicolon-newline-after": "always", 54 | "block-closing-brace-empty-line-before": "never", 55 | "block-closing-brace-newline-after": "always", 56 | "block-closing-brace-newline-before": "always-multi-line", 57 | "block-closing-brace-space-before": "always-single-line", 58 | "block-opening-brace-newline-after": "always-multi-line", 59 | "block-opening-brace-space-after": "always-single-line", 60 | "block-opening-brace-space-before": "always", 61 | "color-hex-case": "lower", 62 | "color-hex-length": "long", 63 | "comment-empty-line-before": [ 64 | "always", 65 | { 66 | "except": [ 67 | "first-nested" 68 | ], 69 | "ignore": [ 70 | "stylelint-commands" 71 | ] 72 | } 73 | ], 74 | "comment-whitespace-inside": "always", 75 | "custom-property-empty-line-before": [ 76 | "always", 77 | { 78 | "except": [ 79 | "after-custom-property", 80 | "first-nested" 81 | ], 82 | "ignore": [ 83 | "after-comment", 84 | "inside-single-line-block" 85 | ] 86 | } 87 | ], 88 | "declaration-bang-space-after": "never", 89 | "declaration-bang-space-before": "always", 90 | "declaration-block-semicolon-newline-after": "always-multi-line", 91 | "declaration-block-semicolon-space-after": "always-single-line", 92 | "declaration-block-semicolon-space-before": "never", 93 | "declaration-block-single-line-max-declarations": 1, 94 | "declaration-block-trailing-semicolon": "always", 95 | "declaration-colon-newline-after": "always-multi-line", 96 | "declaration-colon-space-after": "always-single-line", 97 | "declaration-colon-space-before": "never", 98 | "declaration-empty-line-before": [ 99 | "always", 100 | { 101 | "except": [ 102 | "after-declaration", 103 | "first-nested" 104 | ], 105 | "ignore": [ 106 | "after-comment", 107 | "inside-single-line-block" 108 | ] 109 | } 110 | ], 111 | "function-comma-newline-after": "always-multi-line", 112 | "function-comma-space-after": "always-single-line", 113 | "function-comma-space-before": "never", 114 | "function-max-empty-lines": 0, 115 | "function-name-case": "lower", 116 | "function-parentheses-newline-inside": "always-multi-line", 117 | "function-parentheses-space-inside": "never-single-line", 118 | "function-whitespace-after": "always", 119 | "indentation": 2, 120 | "length-zero-no-unit": true, 121 | "max-empty-lines": 1, 122 | "media-feature-colon-space-after": "always", 123 | "media-feature-colon-space-before": "never", 124 | "media-feature-name-case": "lower", 125 | "media-feature-parentheses-space-inside": "never", 126 | "media-feature-range-operator-space-after": "always", 127 | "media-feature-range-operator-space-before": "always", 128 | "media-query-list-comma-newline-after": "always-multi-line", 129 | "media-query-list-comma-space-after": "always-single-line", 130 | "media-query-list-comma-space-before": "never", 131 | "no-eol-whitespace": true, 132 | "no-missing-end-of-source-newline": true, 133 | "number-leading-zero": "always", 134 | "number-no-trailing-zeros": true, 135 | "property-case": "lower", 136 | "rule-empty-line-before": [ 137 | "always-multi-line", 138 | { 139 | "except": [ 140 | "first-nested" 141 | ], 142 | "ignore": [ 143 | "after-comment" 144 | ] 145 | } 146 | ], 147 | "selector-attribute-brackets-space-inside": "never", 148 | "selector-attribute-operator-space-after": "never", 149 | "selector-attribute-operator-space-before": "never", 150 | "selector-combinator-space-after": "always", 151 | "selector-combinator-space-before": "always", 152 | "selector-descendant-combinator-no-non-space": true, 153 | "selector-list-comma-newline-after": "always", 154 | "selector-list-comma-space-before": "never", 155 | "selector-max-empty-lines": 0, 156 | "selector-pseudo-class-case": "lower", 157 | "selector-pseudo-class-parentheses-space-inside": "never", 158 | "selector-pseudo-element-case": "lower", 159 | "selector-pseudo-element-colon-notation": "double", 160 | "selector-type-case": "lower", 161 | "unit-case": "lower", 162 | "value-list-comma-newline-after": "always-multi-line", 163 | "value-list-comma-space-after": "always-single-line", 164 | "value-list-comma-space-before": "never", 165 | "value-list-max-empty-lines": 0 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tano Systems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tano Systems LuCI theme 2 | This repository contains Tano Systems theme for LuCI web configuration interface. 3 | 4 | This theme is the official theme of the LuCI web configuration interface for [TanoWrt](https://github.com/tano-systems/meta-tano-openwrt) Linux distribution. 5 | 6 | This theme is intended to use [forked LuCI version](https://github.com/tano-systems/luci) by Tano Systems which has a some differences in HTML markup. Therefore, this theme may not work correctly with the [official LuCI](https://github.com/openwrt/luci) and/or official [OpenWrt](https://github.com/openwrt/openwrt) builds. 7 | 8 | The BitBake recipe for this theme can be founded in the [meta-tanowrt](https://github.com/tano-systems/meta-tanowrt/blob/master/meta-tanowrt/recipes-extended/luci-extra/luci-theme-tano.bb) OpenEmbedded layer repository. 9 | 10 | ## Development prerequisites 11 | 12 | ### Node.js and npm 13 | 14 | To start development you should have Node.js installed on your development machine. 15 | 16 | Depending on OS you are using you can obtain appropriate distributive or source code of Node.js from [official site](https://nodejs.org/en/download/). 17 | 18 | Another way to get Node.js installed is by using [NVM](https://github.com/creationix/nvm) (Node Version Manager). 19 | 20 | **It's strongly recommended to have npm of version 5 or higher** to be able to use `package-lock.json` file. 21 | 22 | ## Setting up 23 | 24 | After Node.js and npm are installed you have to take some setup actions. 25 | 26 | ### Installing packages 27 | All packages required for building will be installed automatically. It can take some time. Run this command to install packages. 28 | ``` 29 | npm install 30 | ``` 31 | 32 | After packages are installed you can run theme bundle building. 33 | 34 | ### Configuring env 35 | 36 | To be able to deploy to some host having LuCI up and running you have to setup some environment variables. To get this done do the following (assuming you are in repo's root directory): 37 | ``` 38 | cp ./buildscripts/env.example ./buildscripts/env 39 | ``` 40 | 41 | Created file `./buildscripts/env` contains environment variables required for deployment of built theme to target host. All the variables are documented in place. You may use any other editor instead of `vim`. 42 | ``` 43 | vim ./buildscripts/env 44 | ``` 45 | 46 | After variables are set you may to deploy your changes to configured host. 47 | 48 | ## Running build scripts 49 | 50 | Below described commands that allow you to build and deploy theme. 51 | 52 | ### Building and deploying theme 53 | 54 | To get theme built and deployed you have to run this command 55 | ``` 56 | npm run build_and_deploy 57 | ``` 58 | 59 | Theme will be deployed to target host as soon as get built. 60 | 61 | ### Building theme bundle 62 | 63 | To build a tarball that includes all theme files you have to run this command 64 | ``` 65 | npm run bundle 66 | ``` 67 | 68 | After a tarball get built it will be placed into `bundle` directory. 69 | 70 | ## Screenshots 71 | 72 | ### Desktop 73 | 74 | #### Overview 75 |  76 | 77 | #### System -> General Settings 78 |  79 | 80 | #### Firewall -> Traffic Rules 81 |  82 | 83 | #### UCI Changes 84 |  85 | 86 | ### Mobile 87 | 88 | #### Overview 89 |  90 | 91 | #### Menu 92 |  93 | 94 | #### System -> General Settings 95 |  96 | 97 | #### UCI Changes 98 |  99 | 100 | -------------------------------------------------------------------------------- /buildscripts/bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./vars 4 | 5 | BUNDLE_DIR=${SCRIPT_DIR}/bundle 6 | BUNDLE_BUILD_DIR=${BUNDLE_DIR}/build 7 | TIMESTAMP=`date +"%Y%m%d%H%M%S"` 8 | 9 | mkdir -p ${BUNDLE_BUILD_DIR} 10 | rm -rf ${BUNDLE_BUILD_DIR}/* 11 | 12 | cp -r ${THEME_SRC_DIR}/* ${BUNDLE_BUILD_DIR}/ 13 | cp ${DIST_DIR}/*.css ${BUNDLE_BUILD_DIR}/htdocs/luci-static/tano/ 14 | cp ${THEME_SRC_DIR}/Makefile ${BUNDLE_BUILD_DIR} 15 | 16 | tar -czvf ${BUNDLE_DIR}/${THEME_NAME}-${TIMESTAMP}.tar.gz -C ${BUNDLE_BUILD_DIR} . 17 | -------------------------------------------------------------------------------- /buildscripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./env 4 | source ./vars 5 | 6 | RUN_SSHPASS="" 7 | if [ -n "${TARGET_PASSWORD}" ]; then 8 | RUN_SSHPASS="sshpass -e" 9 | export SSHPASS="${TARGET_PASSWORD}" 10 | fi 11 | 12 | ${RUN_SSHPASS} scp -r -P ${TARGET_HOST_PORT} ${THEME_SRC_DIR}/htdocs/* ${TARGET_HOST}:/www 13 | ${RUN_SSHPASS} scp -r -P ${TARGET_HOST_PORT} ${THEME_SRC_DIR}/luasrc/* ${TARGET_HOST}:/usr/lib/lua/5.1/luci 14 | ${RUN_SSHPASS} scp -r -P ${TARGET_HOST_PORT} ${THEME_SRC_DIR}/root/* ${TARGET_HOST}:/ 15 | ${RUN_SSHPASS} scp -r -P ${TARGET_HOST_PORT} ${DIST_DIR}/*.css ${TARGET_HOST}:/www/luci-static/tano/ 16 | -------------------------------------------------------------------------------- /buildscripts/env.example: -------------------------------------------------------------------------------- 1 | # Target user and host to use for deployment 2 | TARGET_HOST=user@target_host 3 | 4 | # Password (set empty to use key-based auth) 5 | TARGET_PASSWORD=root 6 | 7 | # SSH port on target host 8 | TARGET_HOST_PORT=22 9 | -------------------------------------------------------------------------------- /buildscripts/vars: -------------------------------------------------------------------------------- 1 | THEME_NAME="luci-theme-tano" 2 | 3 | SCRIPT_DIR=$(cd ../ && pwd) 4 | 5 | SRC_DIR=${SCRIPT_DIR}/src 6 | 7 | DIST_DIR=${SCRIPT_DIR}/dist 8 | 9 | THEME_SRC_DIR=${SRC_DIR}/${THEME_NAME} 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openwrt-tano-theme", 3 | "version": "1.0.0", 4 | "description": "Tano LUCI theme", 5 | "main": "index.js", 6 | "dependencies": { 7 | "reset-css": "^3.0.0" 8 | }, 9 | "devDependencies": { 10 | "css-loader": "^0.28.11", 11 | "mini-css-extract-plugin": "^0.4.0", 12 | "node-sass": "^4.9.0", 13 | "postcss-loader": "^2.1.5", 14 | "sass-loader": "^7.0.1", 15 | "style-loader": "^0.21.0", 16 | "stylelint": "^9.10.1", 17 | "stylelint-order": "^3.0.1", 18 | "stylelint-scss": "^3.8.0", 19 | "webpack": ">=4.9.1 <=4.19.x", 20 | "webpack-cli": "^2.1.4" 21 | }, 22 | "scripts": { 23 | "build": "webpack --progress", 24 | "build_and_deploy": "rm -rf ./dist && webpack --progress && cd ./buildscripts && ./deploy.sh", 25 | "bundle": "rm -rf ./dist && webpack --progress && cd ./buildscripts && ./bundle.sh", 26 | "i18n": "cd ./src/luci-theme-tano && mkdir -p po/templates && i18n-scan.pl . > po/templates/tano.pot && i18n-update.pl po" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "ssh://git@git.tano-systems.com:8892/tano/luci-theme-tano.git" 31 | }, 32 | "author": "Victor Polotebnov", 33 | "license": "MIT" 34 | } 35 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require("autoprefixer") 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /screenshots/desktop-firewall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/desktop-firewall.png -------------------------------------------------------------------------------- /screenshots/desktop-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/desktop-overview.png -------------------------------------------------------------------------------- /screenshots/desktop-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/desktop-system.png -------------------------------------------------------------------------------- /screenshots/desktop-uci-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/desktop-uci-changes.png -------------------------------------------------------------------------------- /screenshots/mobile-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/mobile-menu.png -------------------------------------------------------------------------------- /screenshots/mobile-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/mobile-overview.png -------------------------------------------------------------------------------- /screenshots/mobile-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/mobile-system.png -------------------------------------------------------------------------------- /screenshots/mobile-uci-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/screenshots/mobile-uci-changes.png -------------------------------------------------------------------------------- /src/luci-theme-tano/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | LUCI_TITLE:=Tano theme 4 | 5 | include ../../luci.mk 6 | # call BuildPackage - OpenWrt buildroot signature 7 | -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/resources/menu-tano.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 'require baseclass'; 3 | 'require ui'; 4 | 5 | function Menu(data, rootElement) { 6 | this.data = data; 7 | this.rootElement = document.getElementById(rootElement); 8 | this.id = "menu-" + Menu.generateId(); 9 | } 10 | 11 | Menu.prototype.render = function() { 12 | var node = document.createElement("div"); 13 | var overlay = document.createElement("div"); 14 | 15 | node.setAttribute("class", "menu"); 16 | node.dataset.menuId = this.id; 17 | node.appendChild(new MenuNode({ children: this.data.children, isActive: true }).renderChildren()); 18 | 19 | overlay.setAttribute("class", "menu-overlay"); 20 | 21 | this.rootElement.appendChild(overlay); 22 | this.rootElement.appendChild(node); 23 | 24 | document.querySelectorAll("[data-menu-id='" + this.id + "'] .menu-container > .menu-label-wrapper").forEach(function(label) { 25 | label.addEventListener("click", Menu.toggle); 26 | }); 27 | }; 28 | 29 | Menu.generateId = function() { 30 | return Math.round(Math.random() * Math.pow(10, 10)); 31 | }; 32 | 33 | Menu.toggle = function(e) { 34 | Menu.toggleClass(e.currentTarget.parentNode.querySelector("ul"), "expanded"); 35 | Menu.toggleClass(e.currentTarget.parentNode, "open"); 36 | }; 37 | 38 | Menu.toggleClass = function(node, className) { 39 | var currentClasses = (node.getAttribute("class") || '').split(" "); 40 | var classIndex = currentClasses.indexOf(className); 41 | var newClasses = []; 42 | 43 | if (classIndex < 0) { 44 | newClasses = currentClasses; 45 | newClasses.push(className); 46 | } else { 47 | newClasses = currentClasses.filter(function(cls) {return cls !== className}); 48 | } 49 | 50 | node.setAttribute("class", newClasses.join(" ").trim()); 51 | }; 52 | 53 | function MenuNode(data) { 54 | this.data = data; 55 | } 56 | 57 | MenuNode.prototype.render = function() { 58 | var li = document.createElement("li"); 59 | var liClasses = []; 60 | var labelWrapper = document.createElement("span"); 61 | var text = null; 62 | var icon = document.createElement("span"); 63 | var labelClasses = ["menu-label"]; 64 | 65 | labelWrapper.setAttribute("class", "menu-label-wrapper"); 66 | 67 | if (this.data.children && this.data.children.length) { 68 | icon.setAttribute("class", "icon"); 69 | } else { 70 | icon.setAttribute("class", "icon placeholder"); 71 | } 72 | 73 | if (this.data.url) { 74 | text = document.createElement("a"); 75 | text.setAttribute("href", L.url(this.data.url)); 76 | } else { 77 | text = document.createElement("span"); 78 | } 79 | 80 | labelWrapper.appendChild(icon); 81 | labelWrapper.appendChild(text); 82 | li.appendChild(labelWrapper); 83 | 84 | if (this.data.children && this.data.children.length) { 85 | liClasses.push("menu-container"); 86 | li.appendChild(this.renderChildren()); 87 | 88 | if (this.data.isActive) { 89 | var hasChildActive = false; 90 | 91 | liClasses.push("open"); 92 | 93 | for (let i = 0; i < this.data.children.length; i++) { 94 | if (this.data.children[i].isActive) { 95 | hasChildActive = true; 96 | break; 97 | } 98 | } 99 | 100 | if (!hasChildActive) { 101 | labelClasses.push("active"); 102 | } 103 | } 104 | } else if (this.data.isActive) { 105 | labelClasses.push("active"); 106 | } 107 | 108 | text.setAttribute("class", labelClasses.join(" ")); 109 | text.innerText = _(this.data.title); 110 | 111 | if (liClasses.length > 0) { 112 | li.setAttribute("class", liClasses.join(" ")); 113 | } 114 | 115 | return li; 116 | }; 117 | 118 | MenuNode.prototype.renderChildren = function() { 119 | var ul = document.createElement("ul"); 120 | var ulClasses = []; 121 | 122 | if (this.data.isActive) { 123 | ulClasses.push("expanded"); 124 | } 125 | 126 | if (ulClasses.length > 0) { 127 | ul.setAttribute("class", ulClasses.join(" ")); 128 | } 129 | 130 | this.data.children.forEach(function(child) { 131 | ul.appendChild(new MenuNode(child).render()); 132 | }); 133 | 134 | return ul; 135 | }; 136 | 137 | return baseclass.extend({ 138 | __init__: function() { 139 | ui.menu.load().then(L.bind(this.render, this)); 140 | }, 141 | 142 | renderBreadcrumbs: function(breadcrumbs) { 143 | var e = document.querySelector('#menu-breadcrumbs'); 144 | 145 | if (breadcrumbs.length < 2) 146 | return; 147 | 148 | var ul = E('ul', { class: 'breadcrumbs' }); 149 | 150 | for (let i = 1; i < breadcrumbs.length; i++) { 151 | let url = false; 152 | 153 | if ((i < (breadcrumbs.length - 1)) && breadcrumbs[i].url) 154 | url = true; 155 | 156 | ul.appendChild(E('li', { class: 'breadcrumbs-item' }, 157 | url ? E('a', { href: L.url(breadcrumbs[i].url) }, _(breadcrumbs[i].title)) 158 | : _(breadcrumbs[i].title) 159 | )); 160 | } 161 | 162 | e.appendChild(ul); 163 | }, 164 | 165 | renderMenu: function(menu) { 166 | new Menu(menu, "sidemenu").render(); 167 | }, 168 | 169 | render: function(tree) { 170 | var breadcrumbs = []; 171 | 172 | var activeNode = tree; 173 | for (var i = 0; i < L.env.dispatchpath.length; i++) { 174 | if (!activeNode.children || 175 | !activeNode.children.hasOwnProperty(L.env.dispatchpath[i])) 176 | break; 177 | 178 | activeNode = activeNode.children[L.env.dispatchpath[i]]; 179 | activeNode.isActive = true; 180 | 181 | if (activeNode.title) 182 | breadcrumbs.push(activeNode); 183 | } 184 | 185 | if (!i) 186 | return; 187 | 188 | var menu = this.convertMenu(tree, '', 0); 189 | 190 | this.renderMenu(menu.children[0]); 191 | this.renderBreadcrumbs(breadcrumbs); 192 | 193 | var sidemenuToggle = document.getElementById("sidemenu-toggle"); 194 | var sidemenu = document.getElementById("sidemenu"); 195 | var menuOverlay = document.getElementsByClassName("menu-overlay")[0]; 196 | 197 | sidemenuToggle.addEventListener("click", function() { 198 | Menu.toggleClass(sidemenu, "dn"); 199 | Menu.toggleClass(document.body, "menu-opened"); 200 | }); 201 | 202 | if (menuOverlay) { 203 | menuOverlay.addEventListener("click", function() { 204 | sidemenuToggle.click(); 205 | }); 206 | } 207 | }, 208 | 209 | getChildren: function(node) { 210 | var children = []; 211 | for (var k in node.children) { 212 | if (!node.children.hasOwnProperty(k)) 213 | continue; 214 | if (!node.children[k].satisfied) 215 | continue; 216 | if (!node.children[k].hasOwnProperty('title')) 217 | continue; 218 | children.push(Object.assign(node.children[k], { name: k })); 219 | } 220 | return children.sort(function(a, b) { 221 | return ((a.order || 1000) - (b.order || 1000)); 222 | }); 223 | }, 224 | 225 | convertMenu: function(node, url, depth) { 226 | node.children = this.getChildren(node); 227 | 228 | if (node.name) 229 | if (url && url !== '') 230 | node.url = url + '/' + node.name; 231 | else 232 | node.url = node.name; 233 | else 234 | node.url = url; 235 | 236 | for (let i = 0; i < node.children.length; i++) { 237 | node.children[i] = this.convertMenu(node.children[i], node.url, depth + 1); 238 | }; 239 | 240 | if (!node.action || 241 | node.action.recurse || 242 | ((node.action.type == 'alias') && (node.children.length > 0)) || 243 | (node.action.type == "firstchild")) 244 | node.url = null; 245 | 246 | return node; 247 | } 248 | }); 249 | -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fa-bars.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/favicon.png -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Bold.woff -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Bold.woff2 -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-BoldItalic.woff -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Italic.woff -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito-Italic.woff2 -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito.woff -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/htdocs/luci-static/tano/fonts/Carlito.woff2 -------------------------------------------------------------------------------- /src/luci-theme-tano/htdocs/luci-static/tano/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 31 | -------------------------------------------------------------------------------- /src/luci-theme-tano/luasrc/view/themes/tano/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tano-systems/luci-theme-tano/f7c647300351fe255039db2262562f1de395f25d/src/luci-theme-tano/luasrc/view/themes/tano/.keep -------------------------------------------------------------------------------- /src/luci-theme-tano/luasrc/view/themes/tano/footer.htm: -------------------------------------------------------------------------------- 1 | <%# 2 | Created by Victor Polotebnov (v.polotebnov@tano-systems.com) 3 | %> 4 | <% 5 | local ver = require "luci.version" 6 | %> 7 |
14 | 15 | 16 | 17 | 18 |