Website Mockup for X-CPQ Editor with Tailwind and DaisyUI

This commit is contained in:
2026-08-30 22:57:43 +02:00
parent fbd3d63d17
commit ac56c2a8e8
45 changed files with 6858 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
# xcpq Überblick
## Installation
## Build How To
### Open Source - Pakete
#### Go:
##### Viper
Viper dient zum Laden uns Auswerten von Config-Dateien. In diesem Case werden die HTTP-Servereinstellungen und die Datenbankverbindung ausgewertet.
````
go get github.com/spf13/viper
````
##### PostgreSQL
Die Datenbank des Systems ist in dem Datenbankmanagementsystem PostgreSQL abgelegt. Zum einen braucht es eine Installation des Systems, auf dem die Software Zugriff hat und zum anderen benötigt das System die entsprechenden PostgreSQL Treiber für Go.
````
go get github.com/lib/pq
````
##### Gin Gonic
Webserver Library zum Routing der API.
````
go get -u github.com/gin-gonic/gin
go get github.com/gin-contrib/cors
go get github.com/gin-contrib/sessions
````
#### HTML Erweiterungen
ggf. später lokal runter laden ...
##### Tailwind und DaisyUI
Tailwind ist ein CSS Framework, das die Erstellung von Webseiten erleichtert. DaisyUI ist eine Erweiterung von Tailwind, die zusätzliche Komponenten bereitstellt.
````
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
````
##### HTMX
HTMX ist eine JavaScript-Bibliothek, die es ermöglicht, HTML direkt über AJAX-Anfragen zu aktualisieren.
````
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
````
## Konfiguration des Systems
## Starten der Anwendung
## Tests
## Sonstiges
@@ -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"
################################################################
+23
View File
@@ -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",
})
}
+49
View File
@@ -1,3 +1,52 @@
module xcpq_editor module xcpq_editor
go 1.27.0 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
)
+115
View File
@@ -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;
}
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

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;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long