Website Mockup for X-CPQ Editor with Tailwind and DaisyUI
@@ -0,0 +1,17 @@
|
||||
# Database configuration
|
||||
# Connection string is in Go pq driver format:
|
||||
# host=<host> port=<port> user=<databaseUser>
|
||||
# password=<databaseUserPassword> dbname=<databaseName>
|
||||
|
||||
[database]
|
||||
connection_string = "host=192.168.178.50 port=5432 user=tsindt password=XD14ko8d dbname=xcpq sslmode=disable"
|
||||
## connection_string = "host=localhost port=5432 user=tsindt password=XD14ko8d dbname=xcpq sslmode=disable"
|
||||
driver_name = "postgres"
|
||||
|
||||
################################################################
|
||||
# HTTP server configuration
|
||||
|
||||
[http]
|
||||
server_address = ":8088"
|
||||
|
||||
################################################################
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"xcpq_editor/cmd/server"
|
||||
"xcpq_editor/internal/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting XCPQ Editor Service")
|
||||
log.Println("Reading configuration")
|
||||
config := config.InitConfig("config")
|
||||
log.Println("Initializing database")
|
||||
dbHandler := server.InitDatabase(config)
|
||||
log.Println("Initializing HTTP server")
|
||||
httpServer := server.InitHttpServer(config, dbHandler)
|
||||
httpServer.Start()
|
||||
|
||||
log.Println("XCPQ Editor Service Stopped")
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"database/sql"
|
||||
"github.com/spf13/viper"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func InitDatabase(config *viper.Viper) *sql.DB {
|
||||
connectionString := config.GetString("database.connection_string")
|
||||
driverName := config.GetString("database.driver_name")
|
||||
if connectionString == "" {
|
||||
log.Fatalf("Database connection string is missing")
|
||||
}
|
||||
dbHandler, err := sql.Open(driverName, connectionString)
|
||||
if err != nil {
|
||||
log.Fatalf("Error while initializing database: %v", err)
|
||||
}
|
||||
err = dbHandler.Ping()
|
||||
if err != nil {
|
||||
dbHandler.Close()
|
||||
log.Fatalf("Error while validating database: %v", err)
|
||||
}
|
||||
return dbHandler
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type HttpServer struct {
|
||||
config *viper.Viper
|
||||
router *gin.Engine
|
||||
dbHandler *sql.DB
|
||||
}
|
||||
|
||||
func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer {
|
||||
|
||||
// repository initialisation:
|
||||
|
||||
// service initialisation:
|
||||
|
||||
// controller initialisation:
|
||||
|
||||
// Service API
|
||||
router := gin.Default()
|
||||
router.Use(cors.Default())
|
||||
|
||||
// static content
|
||||
router.Static("/static", "../static")
|
||||
router.StaticFile("/", "./static/index.html")
|
||||
|
||||
router.GET("/test", Test)
|
||||
|
||||
// server
|
||||
return HttpServer{
|
||||
config: config,
|
||||
router: router,
|
||||
dbHandler: dbHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func (hs HttpServer) Start() {
|
||||
err := hs.router.Run(hs.config.GetString("http.server_address"))
|
||||
if err != nil {
|
||||
log.Fatalf("Error while starting HTTP server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"message": "Test",
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,52 @@
|
||||
module xcpq_editor
|
||||
|
||||
go 1.27.0
|
||||
|
||||
require (
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/bytedance/sonic v1.15.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.2 // indirect
|
||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
|
||||
github.com/gin-contrib/cors v1.7.7 // indirect
|
||||
github.com/gin-contrib/sessions v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v1.1.1 // indirect
|
||||
github.com/gin-gonic/gin v1.12.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.3 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.5.0 // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.61.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.2 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.30.0 // indirect
|
||||
golang.org/x/crypto v0.55.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.3 h1:P3akjLPBtV/i6bHC6LbcLjY3KuoOvfiqF8wFHeP5IhY=
|
||||
github.com/bytedance/sonic v1.15.3/go.mod h1:8e51yTPdY8M6t+vvGL1c2Y1xL9i+frEeIAQAEl75NUc=
|
||||
github.com/bytedance/sonic/loader v0.5.2 h1:0QtP1gevc1OZ6/H8Lb9BRZiCXd1Ftjd3OKuj1T1lBIo=
|
||||
github.com/bytedance/sonic/loader v0.5.2/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
|
||||
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
||||
github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q=
|
||||
github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE=
|
||||
github.com/gin-contrib/sessions v1.1.0 h1:00mhHfNEGF5sP2fwxa98aRqj1FOJdL6IkR86n2hOiBo=
|
||||
github.com/gin-contrib/sessions v1.1.0/go.mod h1:TyYZDIs6qCQg2SOoYPgMT9pAkmZceVNEJMcv5qbIy60=
|
||||
github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
|
||||
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
|
||||
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA=
|
||||
github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.2 h1:zkEASHHyEClGeURfgNT9PJZVfAbs9oEX9QXggwWNJbc=
|
||||
github.com/ugorji/go/codec v1.3.2/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2 h1:b6o2m7zL8g2URuO8urBedAylxojybKXNZTxgkOcl+2w=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.30.0 h1:sB9h+1gRGa2+LauFSV0tm8bK1J2yo1bx6/Uyi/P6DTU=
|
||||
golang.org/x/arch v0.30.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,19 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func InitConfig(fileName string) *viper.Viper {
|
||||
config := viper.New()
|
||||
config.SetConfigName(fileName)
|
||||
config.AddConfigPath(".")
|
||||
config.AddConfigPath("$HOME")
|
||||
err := config.ReadInConfig()
|
||||
if err != nil {
|
||||
log.Fatal("Error while parsing configuration file! ", err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package models
|
||||
|
||||
type Product struct {
|
||||
Id int
|
||||
Name string
|
||||
Properties []Property
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package models
|
||||
|
||||
type Property struct {
|
||||
Id int
|
||||
Name string
|
||||
Description string
|
||||
Parent_Property *Property
|
||||
Values []Value
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type Property_Type struct {
|
||||
Id int
|
||||
Name string
|
||||
Description string
|
||||
Property Property
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package models
|
||||
|
||||
type Property_Types_Group struct {
|
||||
Id int
|
||||
Name string
|
||||
Description string
|
||||
Property_Types []Property_Type
|
||||
Property_Types_Groups []Property_Types_Group
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type Value struct {
|
||||
Id int
|
||||
Value string
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"xcpq_editor/internal/models"
|
||||
)
|
||||
|
||||
type Property_Repository struct {
|
||||
db *sql.DB
|
||||
vr *Value_Repository
|
||||
}
|
||||
|
||||
func NewPropertyRepository(db *sql.DB, valueRepository *Value_Repository) *Property_Repository {
|
||||
return &Property_Repository{
|
||||
db: db,
|
||||
vr: valueRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *Property_Repository) CreateProperty(
|
||||
property *models.Property,
|
||||
parent_property *models.Property,
|
||||
property_type *models.Property_Type,
|
||||
product *models.Product) (*models.Property, error) {
|
||||
|
||||
// Implement the logic to create a new property in the database
|
||||
var parent_property_id int
|
||||
var property_type_id int
|
||||
var product_id int
|
||||
|
||||
if product != nil {
|
||||
product_id = product.Id
|
||||
} else {
|
||||
product_id = 0
|
||||
}
|
||||
if property_type != nil {
|
||||
property_type_id = property_type.Id
|
||||
} else {
|
||||
property_type_id = 0
|
||||
}
|
||||
|
||||
if parent_property != nil {
|
||||
parent_property_id = parent_property.Id
|
||||
} else {
|
||||
parent_property_id = 0
|
||||
}
|
||||
|
||||
query := `INSERT INTO properties (name, description, parent_property_id, property_type_id, product_id) VALUES ($1, $2, $3, $4, $5) RETURNING id`
|
||||
err := pr.db.QueryRow(query, property.Name, property.Description, parent_property_id, property_type_id, product_id).Scan(&property.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if parent_property_id != 0 {
|
||||
property.Parent_Property = parent_property
|
||||
} else {
|
||||
property.Parent_Property = nil
|
||||
}
|
||||
|
||||
if property_type_id != 0 {
|
||||
property_type.Property = *property
|
||||
}
|
||||
|
||||
if product_id != 0 {
|
||||
product.Properties = append(product.Properties, *property)
|
||||
}
|
||||
|
||||
return property, nil
|
||||
}
|
||||
|
||||
func (pr *Property_Repository) GetPropertyById(id int) (*models.Property, error) {
|
||||
// Implement the logic to retrieve a property by its ID from the database
|
||||
query := `SELECT id, name, description, parent_property_id FROM properties WHERE id = $1`
|
||||
property := &models.Property{}
|
||||
var parent_property_id int
|
||||
err := pr.db.QueryRow(query, id).Scan(&property.Id, &property.Name, &property.Description, &parent_property_id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if parent_property_id != 0 {
|
||||
parent_property, err := pr.GetPropertyById(parent_property_id)
|
||||
if err == nil {
|
||||
property.Parent_Property = parent_property
|
||||
}
|
||||
} else {
|
||||
property.Parent_Property = nil
|
||||
}
|
||||
|
||||
valuePtrs, err := pr.vr.GetValuesFromPropertyId(property.Id)
|
||||
if err != nil {
|
||||
return property, err
|
||||
}
|
||||
|
||||
property.Values = make([]models.Value, 0, len(valuePtrs))
|
||||
for _, value := range valuePtrs {
|
||||
if value != nil {
|
||||
property.Values = append(property.Values, *value)
|
||||
}
|
||||
}
|
||||
|
||||
return property, nil
|
||||
}
|
||||
|
||||
func (pr *Property_Repository) UpdateProperty(property *models.Property) error {
|
||||
// Implement the logic to update an existing property in the database
|
||||
query := `UPDATE properties SET name=$1, description=$2 WHERE id=$3`
|
||||
_, err := pr.db.Exec(query, property.Name, property.Description, property.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (pr *Property_Repository) DeleteProperty(id int) error {
|
||||
// Implement the logic to delete a property from the database
|
||||
// First, delete all values associated with the property
|
||||
err := pr.vr.DeleteValuesFromPropertyId(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `DELETE FROM properties WHERE id=$1`
|
||||
_, err = pr.db.Exec(query, id)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"xcpq_editor/internal/models"
|
||||
)
|
||||
|
||||
type Property_Type_Repository struct {
|
||||
db *sql.DB
|
||||
pr *Property_Repository
|
||||
}
|
||||
|
||||
func NewPropertyTypeRepository(db *sql.DB, propertyRepository *Property_Repository) *Property_Type_Repository {
|
||||
return &Property_Type_Repository{
|
||||
db: db,
|
||||
pr: propertyRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (ptr *Property_Type_Repository) CreatePropertyType(
|
||||
property_type *models.Property_Type,
|
||||
property_type_group *models.Property_Types_Group,
|
||||
property *models.Property) (*models.Property_Type, error) {
|
||||
|
||||
// Implement the logic to create a new property type in the database
|
||||
var property_type_group_id int
|
||||
if property_type_group != nil {
|
||||
property_type_group_id = property_type_group.Id
|
||||
} else {
|
||||
property_type_group_id = 0
|
||||
}
|
||||
query := `INSERT INTO property_types (name, description, property_type_group_id) VALUES ($1, $2, $3) RETURNING id`
|
||||
err := ptr.db.QueryRow(query, property_type.Name, property_type.Description, property_type_group_id).Scan(&property_type.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if property_type_group_id != 0 {
|
||||
property_type_group.Property_Types = append(property_type_group.Property_Types, *property_type)
|
||||
}
|
||||
|
||||
createdProperty, err := ptr.pr.CreateProperty(property, nil, property_type, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
property_type.Property = *createdProperty
|
||||
|
||||
return property_type, nil
|
||||
}
|
||||
|
||||
func (ptr *Property_Type_Repository) GetPropertyTypeById(id int) (*models.Property_Type, error) {
|
||||
// Implement the logic to retrieve a property type by its ID from the database
|
||||
query := "SELECT id, name, description FROM property_types WHERE id = $1"
|
||||
property_type := &models.Property_Type{}
|
||||
err := ptr.db.QueryRow(query, id).Scan(&property_type.Id, &property_type.Name, &property_type.Description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
property, err := ptr.pr.GetPropertyById(property_type.Id)
|
||||
property_type.Property = *property
|
||||
return property_type, nil
|
||||
}
|
||||
|
||||
func (ptr *Property_Type_Repository) UpdatePropertyType(property_type *models.Property_Type) error {
|
||||
// Implement the logic to update the label/description of an existing property type
|
||||
query := `UPDATE property_types SET name=$1, description=$2 WHERE id=$3`
|
||||
_, err := ptr.db.Exec(query, property_type.Name, property_type.Description, property_type.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ptr *Property_Type_Repository) DeletePropertyType(id int) error {
|
||||
// Implement the logic to delete a property type from the database
|
||||
query := `DELETE FROM property_types WHERE id=$1`
|
||||
_, err := ptr.db.Exec(query, id)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"xcpq_editor/internal/models"
|
||||
)
|
||||
|
||||
type Value_Repository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewValueRepository(db *sql.DB) *Value_Repository {
|
||||
return &Value_Repository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) CreateValueForPropertyId(propertyId int, value *models.Value) (*models.Value, error) {
|
||||
// Implement the logic to create a new value for the given propertyId in the database
|
||||
query := `INSERT INTO values (propertyid, value) VALUES ($1, $2) RETURNING id`
|
||||
err := vr.db.QueryRow(query, propertyId, value.Value).Scan(&value.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) DeleteValueFromPropertyId(valueId int, propertyId int) error {
|
||||
// Implement the logic to delete the value from the database for the given valueId and propertyId
|
||||
query := `DELETE FROM values WHERE id=$1 AND propertyid=$2`
|
||||
_, err := vr.db.Exec(query, valueId, propertyId)
|
||||
return err
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) UpdateValue(value *models.Value) error {
|
||||
// Implement the logic to update the value in the database for the given valueId
|
||||
query := `UPDATE values SET value=$1 WHERE id=$2`
|
||||
_, err := vr.db.Exec(query, value.Value, value.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) GetValue(id int) (*models.Value, error) {
|
||||
// Implement the logic to retrieve a value by its ID from the database
|
||||
query := "SELECT id, value FROM values WHERE id = $1"
|
||||
row := vr.db.QueryRow(query, id)
|
||||
value := &models.Value{}
|
||||
err := row.Scan(&value.Id, &value.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) DeleteValuesFromPropertyId(propertyId int) error {
|
||||
// Implement the logic to delete all values from the database for the given propertyId
|
||||
query := `DELETE FROM values WHERE propertyid=$1`
|
||||
_, err := vr.db.Exec(query, propertyId)
|
||||
return err
|
||||
}
|
||||
|
||||
func (vr *Value_Repository) GetValuesFromPropertyId(propertyId int) ([]*models.Value, error) {
|
||||
// Implement the logic to retrieve all values from the database for the given propertyId
|
||||
query := `SELECT id, value FROM values WHERE propertyid=$1`
|
||||
rows, err := vr.db.Query(query, propertyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
values := []*models.Value{}
|
||||
for rows.Next() {
|
||||
value := &models.Value{}
|
||||
err := rows.Scan(&value.ID, &value.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
ul.tree, ul.tree ul {
|
||||
list-style-type: none;
|
||||
background: url(vline.png) repeat-y;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 7px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
li.last {
|
||||
background-image: 'url("css/lastnode.png")';
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
ul.tree ul {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
ul.tree li {
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
line-height: 22px;
|
||||
background: url(node.png) no-repeat;
|
||||
}
|
||||
|
||||
ul.tree li.last {
|
||||
background: #fff url(lastnode.png) no-repeat;
|
||||
}
|
||||
|
||||
img.exp_col {
|
||||
position: absolute;
|
||||
margin-top: 4px;
|
||||
margin-left: -20px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
img.exp_col_empty {
|
||||
position: absolute;
|
||||
margin-top: 4px;
|
||||
margin-left: -20px;
|
||||
vertical-align: sub;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
img.icon_tree {
|
||||
vertical-align: middle;
|
||||
padding-left: 3px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
a.node {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
span.node a {
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
span.node {
|
||||
margin-left: -1px;
|
||||
padding-right: 3px;
|
||||
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
span.node:hover {
|
||||
margin-left: -1px;
|
||||
padding-right: 3px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
background-color: #DCEDFF;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
span.node_selected {
|
||||
margin-left: -2px;
|
||||
padding-right: 3px;
|
||||
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
background-color: #CEFFCE;
|
||||
border: 1px solid #8AE88A;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
span.node_selected a {
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.menu,.sub-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font: 10px Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.menu,
|
||||
.sub-menu {
|
||||
list-style: none;
|
||||
background: #000;
|
||||
}
|
||||
.sub-menu {
|
||||
background: #F1F1F1;
|
||||
}
|
||||
.menu a {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.menu span {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu div {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 0px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.menu .menu_img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.menu img {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
vertical-align: sub;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.menu li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu li:hover {
|
||||
background: aquamarine;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sub-menu li:hover {
|
||||
background: aquamarine;
|
||||
}
|
||||
.menu li:hover > .sub-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 150px;
|
||||
position: absolute;
|
||||
background: #F1F1F1;
|
||||
-webkit-user-select: none; /* Chrome/Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+ */
|
||||
cursor: default;
|
||||
box-shadow: 2px 2px 3px #BDBDBD;
|
||||
}
|
||||
|
||||
|
||||
.sub-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
min-width: 150px;
|
||||
box-shadow: 2px 2px 3px #BDBDBD;
|
||||
}
|
||||
|
||||
.menu .sub-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans;
|
||||
}
|
||||
|
||||
#div_tree {
|
||||
font: 10px Verdana, sans-serif;
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#div_log {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
overflow: scroll;
|
||||
padding:10px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
border: 1px solid gray;
|
||||
font: 10px Verdana, sans-serif;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 950 B |
|
After Width: | Height: | Size: 939 B |
|
After Width: | Height: | Size: 838 B |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 297 B |
|
After Width: | Height: | Size: 879 B |
|
After Width: | Height: | Size: 301 B |
|
After Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 491 B |
|
After Width: | Height: | Size: 573 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 963 B |
|
After Width: | Height: | Size: 755 B |
|
After Width: | Height: | Size: 716 B |
@@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="de" data-theme="autumn">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>X-CPQ by Simply CPQ</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="max-w-10xl mx-auto">
|
||||
<section id="nacigation">
|
||||
<div class="navbar bg-base-100 shadow-sm rounded-box">
|
||||
<div class="flex-1">
|
||||
<a class="btn btn-ghost text-xl">X-CPQ by Simply CPQ 2026 Material Editor</a>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li><a>Properties</a></li>
|
||||
<li><a>Products</a></li>
|
||||
<li><a>Conditions</a></li>
|
||||
<li><a>Translations</a></li>
|
||||
<li><a>Imports</a></li>
|
||||
</ul>
|
||||
|
||||
<label class="swap swap-rotate">
|
||||
<!-- this hidden checkbox controls the state -->
|
||||
<input type="checkbox" class="theme-controller" value="business" />
|
||||
|
||||
<!-- sun icon -->
|
||||
<svg class="swap-off h-6 w-6 fill-current" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
||||
</svg>
|
||||
|
||||
<!-- moon icon -->
|
||||
<svg class="swap-on h-6 w-6 fill-current" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
||||
</svg>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tabs" >
|
||||
<div flex-col lg:flex-row ml-16>
|
||||
<div class="tabs tabs-boxed">
|
||||
<a class="tab tab-active">Properties</a>
|
||||
<a class="tab">Products</a>
|
||||
<a class="tab">Conditions</a>
|
||||
<a class="tab">Translations</a>
|
||||
<a class="tab">Imports</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="content">
|
||||
|
||||
<div class="hero bg-base-200 min-h-screen mt-2 rounded-box">
|
||||
<div class="hero-content flex-col lg:flex-row">
|
||||
|
||||
<div class="breadcrumbs max-w-xs text-sm">
|
||||
<ul>
|
||||
<li>Long text 1</li>
|
||||
<li>Long text 2</li>
|
||||
<li>Long text 3</li>
|
||||
<li>Long text 4</li>
|
||||
<li>Long text 5</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<img alt="Tailwind CSS hero component"
|
||||
src="https://img.daisyui.com/images/stock/photo-1635805737707-575885ab0820.webp"
|
||||
class="max-w-sm rounded-lg shadow-2xl" />
|
||||
<div>
|
||||
<h1 class="text-5xl font-bold">Box Office News!</h1>
|
||||
<p class="py-6">
|
||||
Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem
|
||||
quasi. In deleniti eaque aut repudiandae et a id nisi.
|
||||
</p>
|
||||
<button class="btn btn-primary">Get Started</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="footer">
|
||||
<footer class="footer sm:footer-horizontal footer-center bg-base-300 text-base-content p-4 mt-10 rounded-box">
|
||||
<aside>
|
||||
<p>X-CPQ Copyright © 2026 - All right reserved by Simply CPQ</p>
|
||||
</aside>
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,510 @@
|
||||
///// Creating the tree component
|
||||
// p_div: ID of the div where the tree will be rendered;
|
||||
// p_backColor: Background color of the region where the tree is being rendered;
|
||||
// p_contextMenu: Object containing all the context menus. Set null for no context menu;
|
||||
function createTree(p_div,p_backColor,p_contextMenu) {
|
||||
var tree = {
|
||||
name: 'tree',
|
||||
div: p_div,
|
||||
ulElement: null,
|
||||
childNodes: [],
|
||||
backcolor: p_backColor,
|
||||
contextMenu: p_contextMenu,
|
||||
selectedNode: null,
|
||||
nodeCounter: 0,
|
||||
contextMenuDiv: null,
|
||||
rendered: false,
|
||||
///// Creating a new node
|
||||
// p_text: Text displayed on the node;
|
||||
// p_expanded: True or false, indicating wether the node starts expanded or not;
|
||||
// p_icon: Relative path to the icon displayed with the node. Set null if the node has no icon;
|
||||
// p_parentNode: Reference to the parent node. Set null to create the node on the root;
|
||||
// p_tag: Tag is used to store additional information on the node. All node attributes are visible when programming events and context menu actions;
|
||||
// p_contextmenu: Name of the context menu, which is one of the attributes of the p_contextMenu object created with the tree;
|
||||
createNode: function(p_text,p_expanded, p_icon, p_parentNode,p_tag,p_contextmenu) {
|
||||
v_tree = this;
|
||||
node = {
|
||||
id: 'node_' + this.nodeCounter,
|
||||
text: p_text,
|
||||
icon: p_icon,
|
||||
parent: p_parentNode,
|
||||
expanded : p_expanded,
|
||||
childNodes : [],
|
||||
tag : p_tag,
|
||||
contextMenu: p_contextmenu,
|
||||
elementLi: null,
|
||||
///// Removing the node and all its children
|
||||
removeNode: function() { v_tree.removeNode(this); },
|
||||
///// Expanding or collapsing the node, depending on the expanded value
|
||||
toggleNode: function(p_event) { v_tree.toggleNode(this); },
|
||||
///// Expanding the node
|
||||
expandNode: function(p_event) { v_tree.expandNode(this); },
|
||||
///// Expanding the node and its children recursively
|
||||
expandSubtree: function() { v_tree.expandSubtree(this); },
|
||||
///// Changing the node text
|
||||
// p_text: New text;
|
||||
setText: function(p_text) { v_tree.setText(this,p_text); },
|
||||
///// Collapsing the node
|
||||
collapseNode: function() { v_tree.collapseNode(this); },
|
||||
///// Collapsing the node and its children recursively
|
||||
collapseSubtree: function() { v_tree.collapseSubtree(this); },
|
||||
///// Deleting all child nodes
|
||||
removeChildNodes: function() { v_tree.removeChildNodes(this); },
|
||||
///// Creating a new child node;
|
||||
// p_text: Text displayed;
|
||||
// p_expanded: True or false, indicating wether the node starts expanded or not;
|
||||
// p_icon: Icon;
|
||||
// p_tag: Tag;
|
||||
// p_contextmenu: Context Menu;
|
||||
createChildNode: function(p_text,p_expanded,p_icon,p_tag,p_contextmenu) { return v_tree.createNode(p_text,p_expanded,p_icon,this,p_tag,p_contextmenu); }
|
||||
}
|
||||
|
||||
this.nodeCounter++;
|
||||
|
||||
if (this.rendered) {
|
||||
if (p_parentNode==undefined) {
|
||||
this.drawNode(this.ulElement,node);
|
||||
this.adjustLines(this.ulElement,false);
|
||||
}
|
||||
else {
|
||||
var v_ul = p_parentNode.elementLi.getElementsByTagName("ul")[0];
|
||||
if (p_parentNode.childNodes.length==0) {
|
||||
if (p_parentNode.expanded) {
|
||||
p_parentNode.elementLi.getElementsByTagName("ul")[0].style.display = 'block';
|
||||
v_img = p_parentNode.elementLi.getElementsByTagName("img")[0];
|
||||
v_img.style.visibility = "visible";
|
||||
v_img.src = 'images/collapse.png';
|
||||
v_img.id = 'toggle_off';
|
||||
}
|
||||
else {
|
||||
p_parentNode.elementLi.getElementsByTagName("ul")[0].style.display = 'none';
|
||||
v_img = p_parentNode.elementLi.getElementsByTagName("img")[0];
|
||||
v_img.style.visibility = "visible";
|
||||
v_img.src = 'images/expand.png';
|
||||
v_img.id = 'toggle_on';
|
||||
}
|
||||
}
|
||||
this.drawNode(v_ul,node);
|
||||
this.adjustLines(v_ul,false);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_parentNode==undefined) {
|
||||
this.childNodes.push(node);
|
||||
node.parent=this;
|
||||
}
|
||||
else
|
||||
p_parentNode.childNodes.push(node);
|
||||
|
||||
return node;
|
||||
},
|
||||
///// Render the tree;
|
||||
drawTree: function() {
|
||||
|
||||
this.rendered = true;
|
||||
|
||||
var div_tree = document.getElementById(this.div);
|
||||
div_tree.innerHTML = '';
|
||||
|
||||
ulElement = createSimpleElement('ul',this.name,'tree');
|
||||
this.ulElement = ulElement;
|
||||
|
||||
for (var i=0; i<this.childNodes.length; i++) {
|
||||
this.drawNode(ulElement,this.childNodes[i]);
|
||||
}
|
||||
|
||||
div_tree.appendChild(ulElement);
|
||||
|
||||
this.adjustLines(document.getElementById(this.name),true);
|
||||
|
||||
},
|
||||
///// Drawing the node. This function is used when drawing the Tree and should not be called directly;
|
||||
// p_ulElement: Reference to the UL tag element where the node should be created;
|
||||
// p_node: Reference to the node object;
|
||||
drawNode: function(p_ulElement,p_node) {
|
||||
|
||||
v_tree = this;
|
||||
|
||||
var v_icon = null;
|
||||
|
||||
if (p_node.icon!=null)
|
||||
v_icon = createImgElement(null,'icon_tree',p_node.icon);
|
||||
|
||||
var v_li = document.createElement('li');
|
||||
p_node.elementLi = v_li;
|
||||
|
||||
var v_span = createSimpleElement('span',null,'node');
|
||||
|
||||
var v_exp_col = null;
|
||||
|
||||
if (p_node.childNodes.length == 0) {
|
||||
v_exp_col = createImgElement('toggle_off','exp_col','images/collapse.png');
|
||||
v_exp_col.style.visibility = "hidden";
|
||||
}
|
||||
else {
|
||||
if (p_node.expanded) {
|
||||
v_exp_col = createImgElement('toggle_off','exp_col','images/collapse.png');
|
||||
}
|
||||
else {
|
||||
v_exp_col = createImgElement('toggle_on','exp_col','images/expand.png');
|
||||
}
|
||||
}
|
||||
|
||||
v_span.ondblclick = function() {
|
||||
v_tree.doubleClickNode(p_node);
|
||||
};
|
||||
|
||||
v_exp_col.onclick = function() {
|
||||
v_tree.toggleNode(p_node);
|
||||
};
|
||||
|
||||
v_span.onclick = function() {
|
||||
v_tree.selectNode(p_node);
|
||||
};
|
||||
|
||||
v_span.oncontextmenu = function(e) {
|
||||
v_tree.selectNode(p_node);
|
||||
v_tree.nodeContextMenu(e,p_node);
|
||||
};
|
||||
|
||||
if (v_icon!=undefined)
|
||||
v_span.appendChild(v_icon);
|
||||
|
||||
v_a = createSimpleElement('a',null,null);
|
||||
v_a.innerHTML=p_node.text;
|
||||
v_span.appendChild(v_a);
|
||||
v_li.appendChild(v_exp_col);
|
||||
v_li.appendChild(v_span);
|
||||
|
||||
p_ulElement.appendChild(v_li);
|
||||
|
||||
var v_ul = createSimpleElement('ul','ul_' + p_node.id,null);
|
||||
v_li.appendChild(v_ul);
|
||||
|
||||
if (p_node.childNodes.length > 0) {
|
||||
|
||||
if (!p_node.expanded)
|
||||
v_ul.style.display = 'none';
|
||||
|
||||
for (var i=0; i<p_node.childNodes.length; i++) {
|
||||
this.drawNode(v_ul,p_node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Changing node text
|
||||
// p_node: Reference to the node that will have its text updated;
|
||||
// p_text: New text;
|
||||
setText: function(p_node,p_text) {
|
||||
p_node.elementLi.getElementsByTagName('span')[0].lastChild.innerHTML = p_text;
|
||||
p_node.text = p_text;
|
||||
},
|
||||
///// Expanding all tree nodes
|
||||
expandTree: function() {
|
||||
for (var i=0; i<this.childNodes.length; i++) {
|
||||
if (this.childNodes[i].childNodes.length>0) {
|
||||
this.expandSubtree(this.childNodes[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Expanding all nodes inside the subtree that have parameter 'p_node' as root
|
||||
// p_node: Subtree root;
|
||||
expandSubtree: function(p_node) {
|
||||
this.expandNode(p_node);
|
||||
for (var i=0; i<p_node.childNodes.length; i++) {
|
||||
if (p_node.childNodes[i].childNodes.length>0) {
|
||||
this.expandSubtree(p_node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Collapsing all tree nodes
|
||||
collapseTree: function() {
|
||||
for (var i=0; i<this.childNodes.length; i++) {
|
||||
if (this.childNodes[i].childNodes.length>0) {
|
||||
this.collapseSubtree(this.childNodes[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Collapsing all nodes inside the subtree that have parameter 'p_node' as root
|
||||
// p_node: Subtree root;
|
||||
collapseSubtree: function(p_node) {
|
||||
this.collapseNode(p_node);
|
||||
for (var i=0; i<p_node.childNodes.length; i++) {
|
||||
if (p_node.childNodes[i].childNodes.length>0) {
|
||||
this.collapseSubtree(p_node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Expanding node
|
||||
// p_node: Reference to the node;
|
||||
expandNode: function(p_node) {
|
||||
if (p_node.childNodes.length>0 && p_node.expanded==false) {
|
||||
if (this.nodeBeforeOpenEvent!=undefined)
|
||||
this.nodeBeforeOpenEvent(p_node);
|
||||
|
||||
var img=p_node.elementLi.getElementsByTagName("img")[0];
|
||||
|
||||
p_node.expanded = true;
|
||||
|
||||
img.id="toggle_off";
|
||||
img.src = 'images/collapse.png';
|
||||
elem_ul = img.parentElement.getElementsByTagName("ul")[0];
|
||||
elem_ul.style.display = 'block';
|
||||
|
||||
if (this.nodeAfterOpenEvent!=undefined)
|
||||
this.nodeAfterOpenEvent(p_node);
|
||||
}
|
||||
},
|
||||
///// Collapsing node
|
||||
// p_node: Reference to the node;
|
||||
collapseNode: function(p_node) {
|
||||
if (p_node.childNodes.length>0 && p_node.expanded==true) {
|
||||
var img=p_node.elementLi.getElementsByTagName("img")[0];
|
||||
|
||||
p_node.expanded = false;
|
||||
if (this.nodeBeforeCloseEvent!=undefined)
|
||||
this.nodeBeforeCloseEvent(p_node);
|
||||
|
||||
img.id="toggle_on";
|
||||
img.src = 'images/expand.png';
|
||||
elem_ul = img.parentElement.getElementsByTagName("ul")[0];
|
||||
elem_ul.style.display = 'none';
|
||||
|
||||
}
|
||||
},
|
||||
///// Toggling node
|
||||
// p_node: Reference to the node;
|
||||
toggleNode: function(p_node) {
|
||||
if (p_node.childNodes.length>0) {
|
||||
if (p_node.expanded)
|
||||
p_node.collapseNode();
|
||||
else
|
||||
p_node.expandNode();
|
||||
}
|
||||
},
|
||||
///// Double clicking node
|
||||
// p_node: Reference to the node;
|
||||
doubleClickNode: function(p_node) {
|
||||
this.toggleNode(p_node);
|
||||
},
|
||||
///// Selecting node
|
||||
// p_node: Reference to the node;
|
||||
selectNode: function(p_node) {
|
||||
var span = p_node.elementLi.getElementsByTagName("span")[0];
|
||||
span.className = 'node_selected';
|
||||
if (this.selectedNode!=null && this.selectedNode!=p_node)
|
||||
this.selectedNode.elementLi.getElementsByTagName("span")[0].className = 'node';
|
||||
this.selectedNode = p_node;
|
||||
},
|
||||
///// Deleting node
|
||||
// p_node: Reference to the node;
|
||||
removeNode: function(p_node) {
|
||||
var index = p_node.parent.childNodes.indexOf(p_node);
|
||||
|
||||
if (p_node.elementLi.className=="last" && index!=0) {
|
||||
p_node.parent.childNodes[index-1].elementLi.className += "last";
|
||||
p_node.parent.childNodes[index-1].elementLi.style.backgroundColor = this.backcolor;
|
||||
}
|
||||
|
||||
p_node.elementLi.parentNode.removeChild(p_node.elementLi);
|
||||
p_node.parent.childNodes.splice(index, 1);
|
||||
|
||||
if (p_node.parent.childNodes.length==0) {
|
||||
var v_img = p_node.parent.elementLi.getElementsByTagName("img")[0];
|
||||
v_img.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
},
|
||||
///// Deleting all node children
|
||||
// p_node: Reference to the node;
|
||||
removeChildNodes: function(p_node) {
|
||||
|
||||
if (p_node.childNodes.length>0) {
|
||||
var v_ul = p_node.elementLi.getElementsByTagName("ul")[0];
|
||||
|
||||
var v_img = p_node.elementLi.getElementsByTagName("img")[0];
|
||||
v_img.style.visibility = "hidden";
|
||||
|
||||
p_node.childNodes = [];
|
||||
v_ul.innerHTML = "";
|
||||
}
|
||||
},
|
||||
///// Rendering context menu when mouse right button is pressed over a node. This function should no be called directly
|
||||
// p_event: Event triggered when right clicking;
|
||||
// p_node: Reference to the node;
|
||||
nodeContextMenu: function(p_event,p_node) {
|
||||
if (p_event.button==2) {
|
||||
p_event.preventDefault();
|
||||
p_event.stopPropagation();
|
||||
if (p_node.contextMenu!=undefined) {
|
||||
|
||||
v_tree = this;
|
||||
|
||||
var v_menu = this.contextMenu[p_node.contextMenu];
|
||||
|
||||
var v_div;
|
||||
if (this.contextMenuDiv==null) {
|
||||
v_div = createSimpleElement('ul','ul_cm','menu');
|
||||
document.body.appendChild(v_div);
|
||||
}
|
||||
else
|
||||
v_div = this.contextMenuDiv;
|
||||
|
||||
v_div.innerHTML = '';
|
||||
|
||||
var v_left = p_event.pageX-5;
|
||||
var v_right = p_event.pageY-5;
|
||||
|
||||
v_div.style.display = 'block';
|
||||
v_div.style.position = 'absolute';
|
||||
v_div.style.left = v_left + 'px';
|
||||
v_div.style.top = v_right + 'px';
|
||||
|
||||
for (var i=0; i<v_menu.elements.length; i++) (function(i){
|
||||
|
||||
var v_li = createSimpleElement('li',null,null);
|
||||
|
||||
var v_span = createSimpleElement('span',null,null);
|
||||
v_span.onclick = function () { v_menu.elements[i].action(p_node) };
|
||||
|
||||
var v_a = createSimpleElement('a',null,null);
|
||||
var v_ul = createSimpleElement('ul',null,'sub-menu');
|
||||
|
||||
v_a.appendChild(document.createTextNode(v_menu.elements[i].text));
|
||||
|
||||
v_li.appendChild(v_span);
|
||||
|
||||
if (v_menu.elements[i].icon!=undefined) {
|
||||
var v_img = createImgElement('null','null',v_menu.elements[i].icon);
|
||||
v_li.appendChild(v_img);
|
||||
}
|
||||
|
||||
v_li.appendChild(v_a);
|
||||
v_li.appendChild(v_ul);
|
||||
v_div.appendChild(v_li);
|
||||
|
||||
if (v_menu.elements[i].submenu!=undefined) {
|
||||
var v_span_more = createSimpleElement('div',null,null);
|
||||
v_span_more.appendChild(createImgElement(null,'menu_img','images/right.png'));
|
||||
v_li.appendChild(v_span_more);
|
||||
v_tree.contextMenuLi(v_menu.elements[i].submenu,v_ul,p_node);
|
||||
}
|
||||
|
||||
})(i);
|
||||
|
||||
this.contextMenuDiv = v_div;
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
///// Recursive function called when rendering context menu submenus. This function should no be called directly
|
||||
// p_submenu: Reference to the submenu object;
|
||||
// p_ul: Reference to the UL tag;
|
||||
// p_node: Reference to the node;
|
||||
contextMenuLi : function(p_submenu,p_ul,p_node) {
|
||||
|
||||
v_tree = this;
|
||||
|
||||
for (var i=0; i<p_submenu.elements.length; i++) (function(i){
|
||||
|
||||
var v_li = createSimpleElement('li',null,null);
|
||||
|
||||
var v_span = createSimpleElement('span',null,null);
|
||||
v_span.onclick = function () { p_submenu.elements[i].action(p_node) };
|
||||
|
||||
var v_a = createSimpleElement('a',null,null);
|
||||
var v_ul = createSimpleElement('ul',null,'sub-menu');
|
||||
|
||||
v_a.appendChild(document.createTextNode(p_submenu.elements[i].text));
|
||||
|
||||
v_li.appendChild(v_span);
|
||||
|
||||
if (p_submenu.elements[i].icon!=undefined) {
|
||||
var v_img = createImgElement('null','null',p_submenu.elements[i].icon);
|
||||
v_li.appendChild(v_img);
|
||||
}
|
||||
|
||||
v_li.appendChild(v_a);
|
||||
v_li.appendChild(v_ul);
|
||||
p_ul.appendChild(v_li);
|
||||
|
||||
if (p_submenu.elements[i].p_submenu!=undefined) {
|
||||
var v_span_more = createSimpleElement('div',null,null);
|
||||
v_span_more.appendChild(createImgElement(null,'menu_img','images/right.png'));
|
||||
v_li.appendChild(v_span_more);
|
||||
v_tree.contextMenuLi(p_submenu.elements[i].p_submenu,v_ul,p_node);
|
||||
}
|
||||
|
||||
})(i);
|
||||
},
|
||||
///// Adjusting tree dotted lines. This function should not be called directly
|
||||
// p_node: Reference to the node;
|
||||
adjustLines: function(p_ul,p_recursive) {
|
||||
var tree = p_ul;
|
||||
|
||||
var lists = [];
|
||||
|
||||
if (tree.childNodes.length>0) {
|
||||
lists = [ tree ];
|
||||
|
||||
if (p_recursive) {
|
||||
for (var i = 0; i < tree.getElementsByTagName("ul").length; i++) {
|
||||
var check_ul = tree.getElementsByTagName("ul")[i];
|
||||
if (check_ul.childNodes.length!=0)
|
||||
lists[lists.length] = check_ul;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
var item = lists[i].lastChild;
|
||||
|
||||
while (!item.tagName || item.tagName.toLowerCase() != "li") {
|
||||
item = item.previousSibling;
|
||||
}
|
||||
|
||||
item.className += "last";
|
||||
item.style.backgroundColor = this.backcolor;
|
||||
|
||||
item = item.previousSibling;
|
||||
|
||||
if (item!=null)
|
||||
if (item.tagName.toLowerCase() == "li") {
|
||||
item.className = "";
|
||||
item.style.backgroundColor = 'transparent';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onclick = function() {
|
||||
if (tree.contextMenuDiv!=null)
|
||||
tree.contextMenuDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
//Create a HTML element specified by parameter 'p_type'
|
||||
function createSimpleElement(p_type,p_id,p_class) {
|
||||
element = document.createElement(p_type);
|
||||
if (p_id!=undefined)
|
||||
element.id = p_id;
|
||||
if (p_class!=undefined)
|
||||
element.className = p_class;
|
||||
return element;
|
||||
}
|
||||
|
||||
//Create img element
|
||||
function createImgElement(p_id,p_class,p_src) {
|
||||
element = document.createElement('img');
|
||||
if (p_id!=undefined)
|
||||
element.id = p_id;
|
||||
if (p_class!=undefined)
|
||||
element.className = p_class;
|
||||
if (p_src!=undefined)
|
||||
element.src = p_src;
|
||||
return element;
|
||||
}
|
||||