diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a2f90fa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/Data
+/Public/data
+*[Ss]ecrets*
+/Go/Modules
\ No newline at end of file
diff --git a/Bin/AnP.golang.debug b/Bin/AnP.golang.debug
new file mode 100755
index 0000000..7de58fb
Binary files /dev/null and b/Bin/AnP.golang.debug differ
diff --git a/Bin/AnP.test.golang.debug b/Bin/AnP.test.golang.debug
new file mode 100755
index 0000000..4663f79
Binary files /dev/null and b/Bin/AnP.test.golang.debug differ
diff --git a/Go/Application/AnP.go b/Go/Application/AnP.go
new file mode 100644
index 0000000..c90e6dc
--- /dev/null
+++ b/Go/Application/AnP.go
@@ -0,0 +1,28 @@
+package Application
+
+import (
+ "AnP/Drivers"
+ "AnP/Managers"
+ "AnP/Models"
+ "AnP/Modules"
+ "sync"
+)
+
+func NewAnP(inputs any, wait_group *sync.WaitGroup) Models.AnPModel {
+
+ var anp Models.AnPModel = Models.AnPModel{
+ WaitGroup: wait_group,
+ }
+ var wmarkdown Modules.WMarkDown = Modules.NewWMarkDown()
+
+ anp.Request = Drivers.NewURLPathDriver(anp)
+ anp.Settings = Managers.NewSettingsManager(anp, inputs)
+ anp.I18N = Managers.NewI18NManager(anp, inputs)
+ anp.Attributes = NewAttributes(anp)
+ anp.Components = NewComponents(anp)
+ anp.WMarkDown = &wmarkdown
+ anp.Applications = Managers.NewApplicationsManager(anp)
+ anp.HTTPServers = Managers.NewHTTPServersManager(anp, inputs)
+
+ return anp
+}
diff --git a/Go/Application/Attributes.go b/Go/Application/Attributes.go
new file mode 100644
index 0000000..193dc34
--- /dev/null
+++ b/Go/Application/Attributes.go
@@ -0,0 +1,128 @@
+package Application
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+ "slices"
+ "strings"
+)
+
+type Attributes struct {
+ anp Models.AnPModel
+ without_values []string
+ normals []string
+}
+
+func NewAttributes(anp Models.AnPModel) Attributes {
+
+ var attributes Attributes = Attributes{
+ anp: anp,
+ without_values: []string{
+ "disabled", "readonly",
+ },
+ normals: []string{
+ "src", "class", "title", "id", "alt", "target", "href", "style", "lang",
+ "type", "name", "placeholder", "min", "max", "value", "step", "disabled",
+ "readonly",
+ },
+ }
+
+ return attributes
+}
+
+func (_self Attributes) process_fixed(
+ attributes any,
+ included []string,
+ excluded []string,
+ callback func(name string, value string, has_value bool),
+) {
+ switch group := attributes.(type) {
+ case map[string]any:
+ for name, value := range group {
+
+ var pascal string = strings.ToLower(Utils.Patterns.Replace(name, Utils.Patterns.RE_ATTRIBUTE_BAD_SET_CHARACTERS, "-"))
+
+ if (len(included) == 0 || slices.Contains(included, pascal)) && (len(excluded) == 0 || !slices.Contains(excluded, pascal)) {
+
+ var processed_name string = pascal
+ var processed_value string = ""
+ var has_value bool = value != nil || slices.Contains(_self.without_values, pascal)
+
+ if !slices.Contains(_self.normals, pascal) && !slices.Contains([]string{"data-", "aria-"}, pascal[:5]) {
+ processed_name = "data-" + processed_name
+ }
+
+ if has_value {
+ processed_value = Utils.ToString(value)
+ }
+
+ callback(processed_name, processed_value, has_value)
+
+ }
+
+ }
+ case []any:
+ for _, subgroup := range group {
+ _self.process_fixed(subgroup, included, excluded, callback)
+ }
+ }
+}
+
+func (_self Attributes) process(
+ attributes any,
+ included any,
+ excluded any,
+ callback func(name string, value string, has_value bool),
+) {
+ _self.process_fixed(attributes, Utils.GetPascalKeys(included), Utils.GetPascalKeys(excluded), callback)
+}
+
+func (_self Attributes) Create(attributes any, included any, excluded any) string {
+
+ var html string = ``
+
+ _self.process(attributes, included, excluded, func(name string, value string, has_value bool) {
+ html += ` ` + name
+ if has_value {
+ if strings.Contains(value, "\"") {
+ value = Utils.Base64Encode(value)
+ }
+ html += `="` + value + `"`
+ }
+ })
+
+ return html
+}
+
+func GetClasses(classes any) []string {
+ switch group := classes.(type) {
+ case string:
+ return Utils.Patterns.RE_SPACES.Split(group, -1)
+ case []string:
+ return group
+ }
+ return []string{}
+}
+
+func JoinClasses(sets ...any) string {
+
+ var attributes []string = []string{}
+
+ for _, set := range sets {
+ attributes = append(attributes, GetClasses(set)...)
+ }
+
+ return strings.Join(Utils.GetPascalKeys(attributes), " ")
+}
+
+func AttributesRemove(attributes map[string]any, keys []string) map[string]any {
+
+ for _, key := range keys {
+ _, exists := attributes[key]
+ if exists {
+ delete(attributes, key)
+ }
+ }
+
+ return attributes
+}
diff --git a/Go/Application/Components.go b/Go/Application/Components.go
new file mode 100644
index 0000000..464c319
--- /dev/null
+++ b/Go/Application/Components.go
@@ -0,0 +1,184 @@
+package Application
+
+import (
+ "AnP/ComponentsBox"
+ "AnP/Models"
+ "AnP/Utils"
+ "slices"
+)
+
+type Components struct {
+ anp Models.AnPModel
+ autoclosed []string
+ items map[string]func(parameters ...any) any
+ Licenses ComponentsBox.LicensesComponent
+}
+
+func NewComponents(anp Models.AnPModel) Components {
+
+ var components Components = Components{
+ anp: anp,
+ autoclosed: []string{"img", "hr", "br"},
+ items: map[string]func(parameters ...any) any{},
+ Licenses: ComponentsBox.NewLicensesComponent(anp),
+ }
+
+ components.items["image"] = func(inputs ...any) any {
+ return components.Image(inputs[0])
+ }
+ components.items["licenses"] = func(inputs ...any) any {
+ return components.Licenses.Build(Utils.Get(inputs, Utils.GetPointer([][]any{}))...)
+ }
+ components.items["main_menu"] = func(inputs ...any) any {
+ return components.CreateMainMenu(Utils.Get(inputs, Utils.GetPointer([]any{}))...)
+ }
+
+ return components
+}
+
+func GetItem(item []any) (string, any, any) {
+
+ var l int = len(item)
+ var name string
+ var attributes any
+ var childs any = nil
+
+ if l > 0 {
+ name = Utils.Get[string](item[0], nil)
+ }
+ if l > 1 {
+ attributes = item[1]
+ }
+ if l >= 3 {
+ childs = item[2]
+ }
+
+ return name, attributes, childs
+}
+
+func (_self Components) Set(items ...[]any) string {
+
+ var html string = ``
+
+ for _, subitem := range items {
+
+ tag, attributes, childs := GetItem(subitem)
+ method, is_method := _self.items[tag]
+
+ if is_method && attributes != nil {
+
+ var attributes_processed map[string]any = Utils.GetDictionary(attributes, false)
+
+ if _, is := attributes_processed["built"]; is {
+ is_method = false
+ }
+
+ }
+
+ if is_method {
+ switch results := method(Utils.GetArray(attributes)...).(type) {
+ case string:
+ html += results
+ case []any:
+ html += _self.Set(results)
+ }
+ } else {
+ html += `<` + tag + _self.anp.Attributes.Create(attributes, nil, "build")
+ if slices.Contains(_self.autoclosed, tag) {
+ html += ` />`
+ } else {
+ html += `>`
+ if childs != nil {
+ switch values := any(childs).(type) {
+ case [][]any:
+ html += _self.Set(values...)
+ case []any:
+ html += _self.Set(values)
+ default:
+ html += Utils.ToString(values)
+ }
+ }
+ html += `` + tag + `>`
+ }
+ }
+
+ }
+
+ return html
+}
+
+func (_self Components) Image(inputs any) []any {
+ switch value := inputs.(type) {
+ case []string:
+ inputs = map[string]any{"sources": value}
+ }
+
+ var attributes map[string]any = Utils.GetDictionary(inputs, false)
+ var source_keys []string = []string{"images", "sources", "image", "source", "src"}
+ var images []string = Utils.GetStrings(Utils.GetValue[any](source_keys, attributes, nil))
+
+ attributes = AttributesRemove(attributes, append([]string{
+ "class", "classes", "status", "data_status", "data_i", "data_sources",
+ }, source_keys...))
+
+ return []any{"span", Utils.GetDictionary([]any{attributes, map[string]any{
+ "class": JoinClasses("image", Utils.GetValue[any]([]string{"class", "classes"}, attributes, nil)),
+ "data_status": "unloaded",
+ "data_sources": images,
+ "data_i": 0,
+ }}, true), [][]any{
+ {"img"},
+ {"span"},
+ }}
+}
+
+func (_self Components) LicensesBuild(licenses ...[]any) []any {
+ return _self.Licenses.Build(licenses...)
+}
+
+func (_self Components) CreateMainMenu(items ...any) []any {
+
+ var blocks [][]any = [][]any{}
+
+ for _, item := range items {
+
+ var attributes map[string]any = map[string]any{}
+ var text string = ""
+ var name string = ""
+
+ switch set := item.(type) {
+ case []any:
+
+ var link string = *Utils.GetArgument[string](set, 0, nil)
+ var target string = *Utils.GetArgument(set, 1, Utils.GetPointer("_self"))
+
+ name = *Utils.GetArgument(set, 2, Utils.GetPointer(""))
+ text = _self.anp.I18N.Get([]string{*Utils.GetArgument(set, 3, Utils.GetPointer("")), name}, nil, name)
+
+ attributes["href"] = link
+ attributes["target"] = target
+ if name != "" {
+ attributes["data_i18n"] = name
+ attributes["data_i18n_without"] = true
+ }
+ if text != "" || name != "" {
+ attributes["title"] = text
+ }
+
+ case string, any:
+ attributes["href"] = item
+ }
+
+ blocks = append(blocks, []any{"li", nil, [][]any{
+ {"a", attributes, [][]any{
+ {"span", map[string]any{"data_icon": name}, ""},
+ {"span", map[string]any{"data_i18n": name}, text},
+ }},
+ }})
+
+ }
+
+ return []any{"nav", map[string]any{"class": "main-menu"}, [][]any{
+ {"ul", nil, blocks},
+ }}
+}
diff --git a/Go/ComponentsBox/LicensesComponent.go b/Go/ComponentsBox/LicensesComponent.go
new file mode 100644
index 0000000..9e3223f
--- /dev/null
+++ b/Go/ComponentsBox/LicensesComponent.go
@@ -0,0 +1,199 @@
+package ComponentsBox
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+ "strconv"
+)
+
+type LicensesComponent struct {
+ anp Models.AnPModel
+ items map[string]func(inputs ...any) []any
+}
+
+func NewLicensesComponent(anp Models.AnPModel) LicensesComponent {
+
+ var component LicensesComponent = LicensesComponent{
+ anp: anp,
+ items: map[string]func(inputs ...any) []any{},
+ }
+
+ component.items["gplv3"] = func(inputs ...any) []any {
+ return component.GPLv3()
+ }
+ component.items["cc_by_nc_sa_4"] = func(inputs ...any) []any {
+ return component.CC_BY_NC_SA_4()
+ }
+ component.items["copyright"] = func(inputs ...any) []any {
+ return component.Copyright(
+ *Utils.GetArgument[any](inputs, 0, nil),
+ *Utils.GetArgument[any](inputs, 1, nil),
+ *Utils.GetArgument(inputs, 2, Utils.GetPointer(false)),
+ )
+ }
+
+ return component
+}
+
+func (_self LicensesComponent) Build(licenses ...[]any) []any {
+
+ var blocks [][]any = [][]any{}
+
+ for _, data := range licenses {
+
+ var l int = len(data)
+ var name string
+ var inputs []any = []any{}
+
+ if l > 0 {
+ name = Utils.Get[string](data[0], nil)
+ }
+ if l > 1 {
+ inputs = data[1:]
+ }
+
+ if method, exists := _self.items[name]; exists {
+ blocks = append(blocks, method(inputs...))
+ }
+
+ }
+
+ return []any{"span", map[string]any{
+ "class": "licenses",
+ }, blocks}
+}
+
+func (_self LicensesComponent) GPLv3() []any {
+ return []any{"a", map[string]any{
+ "class": "license gpl-v3",
+ "href": "https://www.gnu.org/licenses/gpl-3.0.txt",
+ "target": "_blank",
+ "title": "GPLv3",
+ }, [][]any{
+ {"span", map[string]any{"class": "text"}, "GPLv3"},
+ {"image", map[string]any{
+ "sources": []any{"https://www.gnu.org/graphics/gplv3-88x31.png"},
+ "alt": "GPLv3",
+ }},
+ }}
+}
+
+func (_self LicensesComponent) CC_BY_NC_SA_4() []any {
+
+ var text string = _self.anp.I18N.Get([]string{
+ "Creative Commons Attribution-NonCommertial-ShareAlike 4.0 International (CC-SA-NC-BY 4.0)",
+ "cc_by_nc_sa_4",
+ }, nil, "")
+
+ return []any{"a", map[string]any{
+ "class": "license cc-by-nc-sa-4",
+ "href": "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ "target": "_blank",
+ "data_i18n": "cc_by_nc_sa_4",
+ "data_i18n_without": true,
+ "title": text,
+ }, [][]any{
+ {"span", map[string]any{"class": "text"}, text},
+ {"image", map[string]any{
+ "sources": []any{"https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png"},
+ "data_i18n": "cc_by_nc_sa_4",
+ "data_i18n_without": true,
+ "alt": text,
+ }},
+ }}
+}
+
+func get_year(value any) string {
+ _float, _ := value.(float64)
+ return strconv.FormatInt(int64(_float), 10)
+}
+
+func (_self LicensesComponent) Copyright(year any, authors any, all_rights_reserved bool) []any {
+
+ var year_string string = ""
+ var authors_string string = ""
+
+ switch value := year.(type) {
+ case []any:
+
+ var l int = len(value)
+
+ switch l {
+ case 0:
+ break
+ case 1:
+ year_string = get_year(value)
+ case 2:
+ year_string = get_year(value[0]) + "-" + get_year(value[1])
+ default:
+ for i, subvalue := range value[0 : l-1] {
+ if i != 0 {
+ year_string += ", "
+ }
+ year_string += get_year(subvalue)
+ }
+ year_string += " & " + get_year(value[l-1])
+ }
+
+ case any:
+ year_string = strconv.FormatInt(Utils.Get[int64](value, nil), 10)
+ }
+
+ switch value := authors.(type) {
+ case string:
+ authors_string = value
+ case []string:
+
+ var l int = len(value)
+
+ switch l {
+ case 0:
+ break
+ case 1:
+ authors_string = value[0]
+ case 2:
+ authors_string = value[0] + " &" + value[1]
+ default:
+ for i, subvalue := range value[0 : l-1] {
+ if i != 0 {
+ authors_string += ", "
+ }
+ authors_string += subvalue
+ }
+ authors_string += " & " + value[l-1]
+ }
+
+ }
+
+ if year_string != "" {
+ year_string = " " + year_string + "."
+ }
+ if authors_string != "" {
+ authors_string = " " + authors_string + "."
+ }
+
+ var variables = map[string]any{
+ "year": year_string,
+ "authors": authors_string,
+ "all_rights_reserved": "",
+ }
+
+ if all_rights_reserved {
+ variables["all_rights_reserved"] = " " + _self.anp.I18N.Get([]string{
+ "All rights reserved.",
+ "all_rights_reserved",
+ }, nil, "")
+ }
+
+ var text string = _self.anp.I18N.Get([]string{
+ "© Copyright{year}{authors}{all_rights_reserved}",
+ "copyright",
+ }, variables, "")
+
+ return []any{"span", map[string]any{
+ "class": "license copyright",
+ "data_i18n": "copyriht",
+ "data_i18n_variables": variables,
+ "title": text,
+ }, text}
+}
diff --git a/Go/Drivers/HTTPDriver.go b/Go/Drivers/HTTPDriver.go
new file mode 100644
index 0000000..ae380ca
--- /dev/null
+++ b/Go/Drivers/HTTPDriver.go
@@ -0,0 +1,101 @@
+package Drivers
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+ "context"
+ "fmt"
+ "mime"
+ "net/http"
+ "strconv"
+ "strings"
+)
+
+type HTTPDriver struct {
+ anp Models.AnPModel
+ host string
+ port int16
+ server *http.Server
+ listener func(writer http.ResponseWriter, request *http.Request)
+ autorun bool
+}
+
+func NewHTTPDriver(anp Models.AnPModel, inputs any) *HTTPDriver {
+ mime.AddExtensionType(".js", "application/javascript")
+
+ var server HTTPDriver = HTTPDriver{
+ anp: anp,
+ host: Utils.Get(anp.Settings.Get([]string{
+ "http_driver_host", "http_host", "host",
+ }, inputs, Utils.GetPointer[any]("")), Utils.GetPointer("")),
+ port: Utils.Get(anp.Settings.Get([]string{
+ "http_driver_port", "http_port", "port",
+ }, inputs, Utils.GetPointer[any](8080)), Utils.GetPointer[int16](8080)),
+ listener: Utils.Get[func(writer http.ResponseWriter, request *http.Request)](anp.Settings.Get([]string{
+ "http_driver_listener", "http_listener", "listener",
+ }, inputs, nil), nil),
+ autorun: Utils.Get(anp.Settings.Get([]string{
+ "http_driver_autorun", "http_autorun", "autorun",
+ }, inputs, Utils.GetPointer[any](true)), Utils.GetPointer(true)),
+ }
+
+ if server.listener == nil {
+ server.listener = server.listen
+ }
+
+ if server.autorun {
+ server.Run()
+ }
+
+ return &server
+}
+
+func (_self HTTPDriver) listen(writer http.ResponseWriter, request *http.Request) {
+
+ var response map[string]any = _self.anp.Applications.Go(request.Host, strings.ToLower(request.Method), request.RequestURI)
+ var response_bytes []byte = []byte{}
+
+ if response["response"] != nil {
+ switch value := response["response"].(type) {
+ case string:
+ response_bytes = []byte(value)
+ case []byte:
+ response_bytes = value
+ }
+ }
+
+ writer.Header().Set("Content-Type", Utils.Get[string](response["mime"], nil))
+ writer.WriteHeader(Utils.Get[int](response["code"], nil))
+ writer.Write(response_bytes)
+
+}
+
+func (_self HTTPDriver) Run() {
+
+ _self.server = &http.Server{
+ Addr: _self.host + ":" + strconv.FormatInt(int64(_self.port), 10),
+ Handler: http.HandlerFunc(_self.listener),
+ }
+
+ _self.anp.WaitGroup.Add(1)
+
+ go func() {
+ if error := _self.server.ListenAndServe(); error != nil {
+ fmt.Printf("Error: %v\n", error)
+ }
+ _self.anp.WaitGroup.Done()
+ }()
+
+}
+
+func (_self HTTPDriver) Stop() {
+
+ context_timeout, cancel := context.WithTimeout(context.Background(), 2000)
+
+ defer cancel()
+
+ if _self.server.Shutdown(context_timeout) != nil {
+ Models.DebugPrint("PASA SHUTDOWN")
+ }
+
+}
diff --git a/Go/Drivers/URLPathDriver.go b/Go/Drivers/URLPathDriver.go
new file mode 100644
index 0000000..472208f
--- /dev/null
+++ b/Go/Drivers/URLPathDriver.go
@@ -0,0 +1,191 @@
+package Drivers
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+ "encoding/json"
+ "mime"
+ "os"
+ "strings"
+ "time"
+)
+
+type URLPathDriver struct {
+ anp Models.AnPModel
+ root_paths []string
+ is_dos bool
+ slash string
+}
+
+func NewURLPathDriver(anp Models.AnPModel) URLPathDriver {
+
+ var driver URLPathDriver = URLPathDriver{
+ anp: anp,
+ root_paths: []string{""},
+ is_dos: false,
+ slash: "/",
+ }
+
+ var path string = GetCurrentDirectory()
+
+ if path != "" {
+
+ driver.is_dos = strings.Contains(path, "\\")
+ driver.root_paths = append(driver.root_paths, path)
+
+ if driver.is_dos {
+ driver.slash = "\\"
+ }
+
+ for range 3 {
+ path = Utils.Patterns.RE_PARENT_PATH.ReplaceAllString(path, "$1")
+ if path == "" {
+ break
+ }
+ driver.root_paths = append(driver.root_paths, path)
+ }
+
+ }
+
+ return driver
+}
+
+func GetCurrentDirectory() string {
+
+ var path string = ""
+
+ executable_path, exception := os.Executable()
+
+ if exception != nil {
+ command_path, exception := os.Getwd()
+ if exception == nil {
+ path = command_path
+ }
+ } else {
+ path = executable_path
+ }
+
+ return path
+}
+
+func (_self URLPathDriver) FixPath(path string) string {
+ return Utils.Patterns.RE_SLASH.ReplaceAllString(path, _self.slash)
+}
+
+func (_self URLPathDriver) GetAbsolutePath(path string) string {
+
+ for _, root := range _self.root_paths {
+
+ var full_path string = root
+
+ if full_path != "" {
+ full_path += _self.slash
+ }
+ full_path = _self.FixPath(full_path + path)
+
+ if _, error := os.Stat(full_path); error == nil {
+ path = full_path
+ break
+ }
+
+ }
+
+ return path
+}
+
+func (_self URLPathDriver) LoadFile(path string) string {
+
+ data, _ := os.ReadFile(_self.GetAbsolutePath(path))
+
+ return string(data)
+}
+
+func (_self URLPathDriver) LoadBinaryFile(path string) []byte {
+
+ data, _ := os.ReadFile(_self.GetAbsolutePath(path))
+
+ return data
+}
+
+func (_self URLPathDriver) LoadJSON(inputs any, callback func(any), only_dictionaries bool) {
+ switch group := inputs.(type) {
+ case map[string]any:
+ callback(group)
+ case []any:
+ if only_dictionaries {
+ for _, subgroup := range group {
+ _self.LoadJSON(subgroup, callback, only_dictionaries)
+ }
+ } else {
+ callback(group)
+ }
+ case string:
+
+ var data any
+
+ if json.Unmarshal([]byte(group), &data) != nil {
+ json.Unmarshal([]byte(_self.LoadFile(group)), &data)
+ }
+
+ _self.LoadJSON(data, callback, only_dictionaries)
+
+ }
+}
+
+func (_self URLPathDriver) GetSlash() string {
+ return _self.slash
+}
+
+func GetExtension(path string) string {
+
+ var matches Utils.PatternMatch = Utils.Patterns.Match(path, Utils.Patterns.RE_EXTENSION)
+
+ if matches.Ok {
+ return matches.Groups[1]
+ }
+ return ""
+}
+
+func GetMime(path string) string {
+
+ var mime_type string = mime.TypeByExtension("." + GetExtension(path))
+
+ if mime_type != "" {
+ return mime_type
+ }
+ return "application/octet-stream"
+}
+
+func PathExists(path string) bool {
+
+ _, error := os.Stat(path)
+
+ return error == nil
+}
+
+func FileExists(path string) bool {
+
+ stat, error := os.Stat(path)
+
+ return error == nil && !stat.IsDir()
+}
+
+func DirectoryExists(path string) bool {
+
+ stat, error := os.Stat(path)
+
+ return error == nil && stat.IsDir()
+}
+
+func LastDateModified(path string) *time.Time {
+
+ stat, error := os.Stat(path)
+
+ if error == nil {
+
+ var date time.Time = stat.ModTime()
+
+ return &date
+ }
+ return nil
+}
diff --git a/Go/Interfaces/ApplicationsInterface.go b/Go/Interfaces/ApplicationsInterface.go
new file mode 100644
index 0000000..a1b962e
--- /dev/null
+++ b/Go/Interfaces/ApplicationsInterface.go
@@ -0,0 +1,5 @@
+package Interfaces
+
+type ApplicationsInterface interface {
+ Go(domain string, method string, url string) map[string]any
+}
diff --git a/Go/Interfaces/AttributesInterface.go b/Go/Interfaces/AttributesInterface.go
new file mode 100644
index 0000000..4012d10
--- /dev/null
+++ b/Go/Interfaces/AttributesInterface.go
@@ -0,0 +1,5 @@
+package Interfaces
+
+type AttributesInterface interface {
+ Create(attributes any, included any, excluded any) string
+}
diff --git a/Go/Interfaces/ComponentsInterface.go b/Go/Interfaces/ComponentsInterface.go
new file mode 100644
index 0000000..f9393f8
--- /dev/null
+++ b/Go/Interfaces/ComponentsInterface.go
@@ -0,0 +1,8 @@
+package Interfaces
+
+type ComponentsInterface interface {
+ Set(items ...[]any) string
+ Image(inputs any) []any
+ LicensesBuild(licenses ...[]any) []any
+ CreateMainMenu(items ...any) []any
+}
diff --git a/Go/Interfaces/HTTPServersInterface.go b/Go/Interfaces/HTTPServersInterface.go
new file mode 100644
index 0000000..ea98d67
--- /dev/null
+++ b/Go/Interfaces/HTTPServersInterface.go
@@ -0,0 +1,5 @@
+package Interfaces
+
+type HTTPServersInterfaces interface {
+ // Add(inputs any, overwrite bool)
+}
diff --git a/Go/Interfaces/I18NInterface.go b/Go/Interfaces/I18NInterface.go
new file mode 100644
index 0000000..daecd03
--- /dev/null
+++ b/Go/Interfaces/I18NInterface.go
@@ -0,0 +1,7 @@
+package Interfaces
+
+type I18NInterface interface {
+ GetRaw(texts any, _default string) string
+ Get(keys any, inputs any, _default string) string
+ // Add(inputs any, overwrite bool, header string)
+}
diff --git a/Go/Interfaces/RoutesInterface.go b/Go/Interfaces/RoutesInterface.go
new file mode 100644
index 0000000..67096ea
--- /dev/null
+++ b/Go/Interfaces/RoutesInterface.go
@@ -0,0 +1,6 @@
+package Interfaces
+
+type RoutesInterface interface {
+ // Add(inputs any, overwrite bool)
+ Go(domain string, method string, url string, settings map[string]any) (map[string]any, bool)
+}
diff --git a/Go/Interfaces/SettingsInterface.go b/Go/Interfaces/SettingsInterface.go
new file mode 100644
index 0000000..2312927
--- /dev/null
+++ b/Go/Interfaces/SettingsInterface.go
@@ -0,0 +1,7 @@
+package Interfaces
+
+type SettingsInterface interface {
+ Get(keys any, inputs any, _default *any) any
+ // Add(inputs any, overwrite bool, header string)
+ // AddSecrets(inputs any, overwrite bool, header string)
+}
diff --git a/Go/Interfaces/URLPathInterface.go b/Go/Interfaces/URLPathInterface.go
new file mode 100644
index 0000000..4cdc729
--- /dev/null
+++ b/Go/Interfaces/URLPathInterface.go
@@ -0,0 +1,10 @@
+package Interfaces
+
+type URLPathInterface interface {
+ FixPath(path string) string
+ GetAbsolutePath(path string) string
+ LoadFile(path string) string
+ LoadBinaryFile(path string) []byte
+ LoadJSON(inputs any, callback func(any), only_dictionaries bool)
+ GetSlash() string
+}
diff --git a/Go/Main/Server/Main.go b/Go/Main/Server/Main.go
new file mode 100644
index 0000000..e15376b
--- /dev/null
+++ b/Go/Main/Server/Main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "AnP/Application"
+ "sync"
+)
+
+func main() {
+
+ var wait_group sync.WaitGroup
+
+ Application.NewAnP(nil, &wait_group)
+
+ wait_group.Wait()
+
+}
diff --git a/Go/Main/Test/Main.go b/Go/Main/Test/Main.go
new file mode 100644
index 0000000..a490764
--- /dev/null
+++ b/Go/Main/Test/Main.go
@@ -0,0 +1,10 @@
+package main
+
+import "AnP/Tests"
+
+func main() {
+
+ // var http Tests.HTTPServerTest = Tests.NewHTTPServerTest()
+ Tests.NewHTTPServerTest()
+
+}
diff --git a/Go/Managers/ApplicationsManager.go b/Go/Managers/ApplicationsManager.go
new file mode 100644
index 0000000..effc504
--- /dev/null
+++ b/Go/Managers/ApplicationsManager.go
@@ -0,0 +1,53 @@
+package Managers
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+)
+
+type ApplicationsManager struct {
+ anp Models.AnPModel
+ applications map[string]Models.ApplicationModel
+}
+
+func NewApplicationsManager(anp Models.AnPModel) ApplicationsManager {
+
+ var module ApplicationsManager = ApplicationsManager{
+ anp: anp,
+ applications: map[string]Models.ApplicationModel{},
+ }
+
+ for _, key := range []string{
+ "default_applications_files", "applications_files",
+ "default_applications", "applications",
+ } {
+ module.Add(anp.Settings.Get(key, nil, nil), true)
+ }
+
+ return module
+}
+
+func (_self *ApplicationsManager) Add(inputs any, overwrite bool) {
+ _self.anp.Request.LoadJSON(inputs, func(response any) {
+ for key, data := range Utils.Get[map[string]any](inputs, nil) {
+ _, exists := _self.applications[key]
+ if overwrite || !exists {
+ _self.applications[key] = Models.NewApplicationModel(_self.anp, NewRoutesManager(_self.anp, data), data)
+ }
+ }
+ }, true)
+}
+
+func (_self ApplicationsManager) Go(domain string, method string, url string) map[string]any {
+
+ var response map[string]any = GetDefaultHTTPResponse()
+ var done bool
+
+ for _, application := range _self.applications {
+ if response, done = application.Routes.Go(domain, method, url, application.GetAttributes()); done {
+ break
+ }
+ }
+
+ return response
+}
diff --git a/Go/Managers/HTTPServersManager.go b/Go/Managers/HTTPServersManager.go
new file mode 100644
index 0000000..2533081
--- /dev/null
+++ b/Go/Managers/HTTPServersManager.go
@@ -0,0 +1,44 @@
+package Managers
+
+import (
+ "AnP/Drivers"
+ "AnP/Models"
+)
+
+type HTTPServersManager struct {
+ anp Models.AnPModel
+ servers map[string]*Drivers.HTTPDriver
+}
+
+func NewHTTPServersManager(anp Models.AnPModel, inputs any) HTTPServersManager {
+
+ var manager HTTPServersManager = HTTPServersManager{
+ anp: anp,
+ servers: map[string]*Drivers.HTTPDriver{},
+ }
+
+ for _, key := range []string{
+ "default_http_servers_files", "default_http_servers", "http_servers_files", "http_servers",
+ } {
+ manager.Add(anp.Settings.Get(key, nil, nil), false)
+ }
+
+ return manager
+}
+
+func (_self *HTTPServersManager) Add(inputs any, overwrite bool) {
+ _self.anp.Request.LoadJSON(inputs, func(response any) {
+ switch group := response.(type) {
+ case map[string]any:
+ for key, data := range group {
+ _, exists := _self.servers[key]
+ if overwrite || !exists {
+ switch subinputs := data.(type) {
+ case map[string]any:
+ _self.servers[key] = Drivers.NewHTTPDriver(_self.anp, subinputs)
+ }
+ }
+ }
+ }
+ }, true)
+}
diff --git a/Go/Managers/I18NManager.go b/Go/Managers/I18NManager.go
new file mode 100644
index 0000000..c4d461c
--- /dev/null
+++ b/Go/Managers/I18NManager.go
@@ -0,0 +1,98 @@
+package Managers
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+ "slices"
+ "strings"
+)
+
+type I18NManager struct {
+ anp Models.AnPModel
+ default_language string
+ language_selected string
+ sentences map[string]map[string]any
+}
+
+func NewI18NManager(anp Models.AnPModel, inputs any) I18NManager {
+
+ var i18n I18NManager = I18NManager{
+ anp: anp,
+ default_language: "english",
+ language_selected: "english",
+ sentences: map[string]map[string]any{},
+ }
+
+ for _, key := range []string{"default_i18n_files", "default_i18n", "i18n_files", "i18n"} {
+ i18n.Add(anp.Settings.Get(key, nil, nil), true, "AnP")
+ }
+
+ return i18n
+}
+
+func (_self *I18NManager) Add(inputs any, overwrite bool, header string) {
+ _self.anp.Request.LoadJSON(inputs, func(data any) {
+ for language, sentences := range Utils.Get[map[string]map[string]any](data, nil) {
+ if _, exists := _self.sentences[language]; !exists {
+ _self.sentences[language] = map[string]any{}
+ }
+ for key, sentence := range sentences {
+ if Utils.IsAnPKey(key, header) {
+ _, exists := _self.sentences[language][key]
+ if overwrite || !exists {
+ _self.sentences[language][key] = sentence
+ }
+ }
+ }
+ }
+ }, true)
+}
+
+func (_self I18NManager) GetRaw(texts any, _default string) string {
+
+ var keys []string = Utils.GetKeys(texts)
+
+ if len(keys) != 0 {
+
+ var done []string = []string{}
+
+ for _, language := range append(
+ []string{_self.language_selected, _self.default_language},
+ Utils.GetMapKeys(_self.sentences)...,
+ ) {
+ if !slices.Contains(done, language) {
+ if _, exists := _self.sentences[language]; !exists {
+ for _, key := range keys {
+ if _, exists := _self.sentences[language][key]; exists {
+ switch text := _self.sentences[language][key].(type) {
+ case string:
+ return text
+ case []string:
+ return strings.Join(text, "")
+ }
+ return _default
+ }
+ }
+ }
+ done = append(done, language)
+ }
+ }
+ }
+
+ var text_options []string = Utils.GetStrings(texts)
+
+ if len(text_options) != 0 {
+ return text_options[0]
+ }
+ return _default
+}
+
+func (_self I18NManager) Get(texts any, inputs any, _default string) string {
+
+ var text string = _self.GetRaw(texts, _default)
+
+ if inputs == nil {
+ return text
+ }
+ return Utils.StringVariables(text, inputs, nil)
+}
diff --git a/Go/Managers/RoutesManager.go b/Go/Managers/RoutesManager.go
new file mode 100644
index 0000000..32af9aa
--- /dev/null
+++ b/Go/Managers/RoutesManager.go
@@ -0,0 +1,207 @@
+package Managers
+
+import (
+ "AnP/Drivers"
+ "AnP/Models"
+ "AnP/Modules"
+ "AnP/Utils"
+ "maps"
+ "slices"
+ "time"
+)
+
+type RoutesManager struct {
+ anp Models.AnPModel
+ domains []string
+ routes []Models.RoutesModel
+ indexes []string
+ wmarkdown_html_file string
+}
+
+var default_indexes []string = []string{"index.w.md", "index.md", "index.html", "index.htm"}
+var default_wmarkdown_html string = "/HTML/AnP.wmarkdown.html"
+
+func GetDefaultHTTPResponse() map[string]any {
+ return map[string]any{
+ "ok": false,
+ "code": 502,
+ }
+}
+
+func NewRoutesManager(anp Models.AnPModel, inputs any) RoutesManager {
+
+ var manager RoutesManager = RoutesManager{
+ anp: anp,
+ domains: Utils.GetStrings(anp.Settings.Get([]string{
+ "default_routes_domains", "default_routes_domain",
+ "default_domains", "default_domain",
+ "domains", "domain",
+ }, inputs, nil)),
+ routes: []Models.RoutesModel{},
+ indexes: Utils.GetStrings(anp.Settings.Get([]string{}, inputs, Utils.GetPointer[any](default_indexes))),
+ wmarkdown_html_file: Utils.Get(anp.Settings.Get("wmarkdown_html", inputs, Utils.GetPointer[any](default_wmarkdown_html)), &default_wmarkdown_html),
+ }
+
+ for _, key := range []string{"default_routes_files", "default_routes", "routes_files", "routes"} {
+ manager.Add(anp.Settings.Get(key, inputs, nil), true)
+ }
+
+ return manager
+}
+
+func (_self *RoutesManager) Add(inputs any, overwrite bool) {
+ switch group := inputs.(type) {
+ case string:
+
+ var route Models.RoutesModel = Models.NewRoutesModel(group)
+
+ if route.Ok {
+
+ var i int = -1
+ var exists bool = false
+
+ for _, current := range _self.routes {
+ if current.IsSame(route) {
+ i = current.I
+ exists = true
+ break
+ }
+ }
+
+ if overwrite || !exists {
+ if exists {
+ _self.routes[i] = route
+ route.I = i
+ } else {
+ route.I = len(_self.routes)
+ _self.routes = append(_self.routes, route)
+ }
+ }
+
+ } else {
+ _self.anp.Request.LoadJSON(group, func(data any) {
+ _self.Add(data, overwrite)
+ }, false)
+ }
+
+ case []any:
+ for _, subinputs := range group {
+ _self.Add(subinputs, overwrite)
+ }
+ }
+}
+
+func (_self RoutesManager) StringVariables(_string string, inputs any) string {
+
+ var dictionary map[string]any = Utils.GetDictionary(inputs, false)
+
+ return Utils.Patterns.Replace(_string, Utils.Patterns.RE_VIEWS_STRING_VARIABLES, func(matches Utils.PatternMatch) string {
+ if matches.Groups[1] != "" {
+ if value, exists := dictionary[matches.Groups[1]]; exists {
+ return Utils.ToString(value)
+ }
+ } else if matches.Groups[2] != "" {
+ return _self.anp.I18N.Get(Utils.StringVariables(matches.Groups[2], inputs, nil), inputs, matches.Groups[0])
+ }
+ return matches.Groups[0]
+ })
+}
+
+func (_self RoutesManager) Go(domain string, method string, url string, settings map[string]any) (map[string]any, bool) {
+
+ var response map[string]any = GetDefaultHTTPResponse()
+ var done bool = false
+
+ if slices.Contains(_self.domains, domain) || slices.Contains(_self.domains, "") {
+
+ response["code"] = 404
+ done = true
+
+ for _, route := range _self.routes {
+
+ var matches Utils.PatternMatch = Utils.Patterns.Match(url, route.URL)
+
+ if matches.Ok {
+
+ var variables map[string]any = route.GetVariables(matches)
+
+ if route.Type == Models.RouteTypePath {
+ switch subpath := variables["path"].(type) {
+ case string:
+
+ var slash string = _self.anp.Request.GetSlash()
+
+ subpath = route.Path + slash + subpath
+
+ var full_path string = _self.anp.Request.GetAbsolutePath(subpath)
+ var ok bool = Drivers.FileExists(full_path)
+
+ if !ok {
+
+ for _, index := range _self.indexes {
+
+ var temporary_path string = _self.anp.Request.GetAbsolutePath(subpath + slash + index)
+
+ ok = Drivers.FileExists(temporary_path)
+ if ok {
+ full_path = temporary_path
+ break
+ }
+
+ }
+
+ }
+
+ if ok {
+ if full_path[len(full_path)-3:] == ".md" {
+
+ html, subvariables := _self.anp.WMarkDown.Process(_self.anp.Request.LoadFile(full_path), Modules.WMD.HTML, full_path)
+ var last_date_modified time.Time = *Drivers.LastDateModified(full_path)
+
+ variables["contents"] = html
+ variables["version"] = last_date_modified.Format("20060102")
+ maps.Copy(variables, settings)
+ for key, value := range subvariables {
+ variables[key] = value
+ }
+
+ response["ok"] = true
+ response["code"] = 200
+ response["mime"] = "text/html;charset=" + variables["charset"].(string)
+ response["response"] = _self.StringVariables(_self.anp.Request.LoadFile(_self.wmarkdown_html_file), variables)
+
+ } else {
+
+ var mime_type string = Drivers.GetMime(full_path)
+ var data []byte = _self.anp.Request.LoadBinaryFile(full_path)
+
+ response["ok"] = true
+ response["code"] = 200
+ response["mime"] = mime_type
+ if mime_type[:5] == "text/" {
+ response["response"] = string(data)
+ } else {
+ response["response"] = data
+ }
+
+ }
+ }
+
+ default:
+ response["code"] = 501
+ }
+ } else if route.Type == Models.RouteTypeView {
+ response["code"] = 503
+ } else if route.Type == Models.RouteTypeController {
+ response["code"] = 503
+ } else {
+ response["code"] = 500
+ }
+ }
+
+ }
+
+ }
+
+ return response, done
+}
diff --git a/Go/Managers/SettingsManager.go b/Go/Managers/SettingsManager.go
new file mode 100644
index 0000000..87b5386
--- /dev/null
+++ b/Go/Managers/SettingsManager.go
@@ -0,0 +1,80 @@
+package Managers
+
+import (
+ "AnP/Models"
+ "AnP/Utils"
+)
+
+var default_settings map[string]any = map[string]any{
+ "default_settings_files": []any{
+ "/Public/json/AnP.go.settings.json",
+ "/JSON/AnP.go.settings.json",
+ "AnP.go.settings.json",
+ },
+ "default_secrets_files": []any{
+ "/Public/json/AnP.go.secrets.json",
+ "/JSON/AnP.go.secrets.json",
+ "AnP.go.secrets.json",
+ },
+}
+
+type SettingsManager struct {
+ anp Models.AnPModel
+ inputs map[string]any
+ settings map[string]any
+ secrets map[string]any
+}
+
+func NewSettingsManager(anp Models.AnPModel, inputs any) SettingsManager {
+
+ var settings SettingsManager = SettingsManager{
+ anp: anp,
+ inputs: Utils.GetDictionary(inputs, true),
+ settings: map[string]any{},
+ secrets: map[string]any{},
+ }
+
+ for _, key := range []string{"default_settings_files", "default_settings", "settings_files", "settings"} {
+ settings.Add(settings.Get(key, nil, nil), true, "AnP")
+ }
+
+ for _, key := range []string{"default_secrets_files", "default_secrets", "secrets_files", "secrets"} {
+ settings.AddSecrets(settings.Get(key, nil, nil), true, "AnP")
+ }
+
+ return settings
+}
+
+func GetSettings[T any](_self SettingsManager, keys any, inputs any, _default *T) T {
+ return Utils.GetValue(keys, []any{inputs, _self.secrets, _self.inputs, _self.settings, default_settings}, _default)
+}
+
+func (_self SettingsManager) Get(keys any, inputs any, _default *any) any {
+ return GetSettings(_self, keys, inputs, _default)
+}
+
+func (_self *SettingsManager) Add(inputs any, overwrite bool, header string) {
+ _self.anp.Request.LoadJSON(inputs, func(data any) {
+ for key, value := range Utils.Get[map[string]any](data, nil) {
+ if !Utils.IsAnPKey(key, header) {
+ _, exists := _self.settings[key]
+ if overwrite || !exists {
+ _self.settings[key] = value
+ }
+ }
+ }
+ }, true)
+}
+
+func (_self *SettingsManager) AddSecrets(inputs any, overwrite bool, header string) {
+ _self.anp.Request.LoadJSON(inputs, func(data any) {
+ for key, value := range Utils.Get[map[string]any](data, nil) {
+ if !Utils.IsAnPKey(key, header) {
+ _, exists := _self.secrets[key]
+ if overwrite || !exists {
+ _self.secrets[key] = value
+ }
+ }
+ }
+ }, true)
+}
diff --git a/Go/Models/AnPModel.go b/Go/Models/AnPModel.go
new file mode 100644
index 0000000..2462400
--- /dev/null
+++ b/Go/Models/AnPModel.go
@@ -0,0 +1,19 @@
+package Models
+
+import (
+ "AnP/Interfaces"
+ "AnP/Modules"
+ "sync"
+)
+
+type AnPModel struct {
+ WaitGroup *sync.WaitGroup
+ Request Interfaces.URLPathInterface
+ Settings Interfaces.SettingsInterface
+ I18N Interfaces.I18NInterface
+ Attributes Interfaces.AttributesInterface
+ Components Interfaces.ComponentsInterface
+ Applications Interfaces.ApplicationsInterface
+ WMarkDown *Modules.WMarkDown
+ HTTPServers Interfaces.HTTPServersInterfaces
+}
diff --git a/Go/Models/ApplicationModel.go b/Go/Models/ApplicationModel.go
new file mode 100644
index 0000000..beab90a
--- /dev/null
+++ b/Go/Models/ApplicationModel.go
@@ -0,0 +1,115 @@
+package Models
+
+import (
+ "AnP/Interfaces"
+ "AnP/Utils"
+ "maps"
+ "slices"
+ "strings"
+)
+
+type ApplicationModel struct {
+ anp AnPModel
+ Attributes map[string]any
+ Routes Interfaces.RoutesInterface
+}
+
+func SetClasses(classes []string) string {
+
+ if !slices.Contains(classes, "anp") {
+ classes = append(classes, "anp")
+ }
+
+ return strings.Join(classes, " ")
+}
+
+func NewApplicationModel(anp AnPModel, routes Interfaces.RoutesInterface, inputs any) ApplicationModel {
+
+ var attributes map[string]any = map[string]any{}
+ var strings_sets map[string][]string = map[string][]string{}
+ var l int
+ var licenses [][]any = [][]any{}
+ var menu []any = []any{}
+
+ for _, license := range Utils.GetValue[[]any]([]string{
+ "application_licenses", "application_license", "licenses", "license",
+ }, inputs, nil) {
+ licenses = append(licenses, Utils.Get[[]any](license, nil))
+ }
+
+ for _, item := range Utils.GetValue[[]any]([]string{
+ "application_menu", "menu",
+ }, inputs, nil) {
+ menu = append(menu, Utils.Get[any](item, nil))
+ }
+
+ for key, keys := range map[string][]string{
+ "git": {"application_git", "git"},
+ "project": {"application_name", "name", "project"},
+ "web": {"application_link", "application_url", "application_web", "link", "url", "web"},
+ "logo": {"application_logo", "logo"},
+ "snake": {"application_snake", "snake"},
+ } {
+ attributes[key] = Utils.GetValue[string](keys, inputs, nil)
+ }
+
+ switch logo := attributes["logo"].(type) {
+ case []string, []any:
+ attributes["logo_sources"] = logo
+ case string, any:
+ attributes["logo_sources"] = []any{logo}
+ default:
+ attributes["logo_sources"] = []any{}
+ }
+ attributes["logo_sources"] = Utils.VariablesEncode(attributes["logo_sources"])
+
+ for key, keys := range map[string][][]string{
+ "charset": {{"charset"}, {"utf-8"}},
+ } {
+ attributes[key] = Utils.GetValue(keys[0], inputs, &keys[1][0])
+ }
+
+ for key, keys := range map[string][]string{
+ "class": {"application_class", "class"},
+ "authors": {"application_authors", "application_author", "authors", "author"},
+ "scripts": {"application_scripts", "application_script", "scripts", "script"},
+ "styles": {"application_styles", "application_style", "styles", "style"},
+ "dictionaries": {"application_dictionaries", "application_dictionary", "dictionaries", "dictionary"},
+ "menu": {"application_menu", "menu"},
+ } {
+ strings_sets[key] = Utils.GetStringsFilled(Utils.GetValue[any](keys, inputs, nil))
+ }
+
+ attributes["class"] = SetClasses(strings_sets["class"])
+ for _, key := range []string{"scripts", "styles", "dictionaries"} {
+ attributes[key] = Utils.JSONEncode(strings_sets[key])
+ }
+
+ l = len(strings_sets["authors"])
+ switch l {
+ case 0:
+ attributes["authors"] = ""
+ case 1:
+ attributes["authors"] = strings_sets["authors"][0]
+ case 2:
+ attributes["authors"] = strings.Join(strings_sets["authors"], " & ")
+ default:
+ attributes["authors"] = strings.Join(strings_sets["authors"][:l-1], ", ") + " & " + strings_sets["authors"][l-1]
+ }
+
+ attributes["licenses"] = licenses
+ attributes["licenses_html"] = anp.Components.Set(anp.Components.LicensesBuild(licenses...))
+
+ attributes["menu"] = menu
+ attributes["menu_html"] = anp.Components.Set(anp.Components.CreateMainMenu(menu...))
+
+ return ApplicationModel{
+ anp: anp,
+ Attributes: attributes,
+ Routes: routes,
+ }
+}
+
+func (_self ApplicationModel) GetAttributes() map[string]any {
+ return maps.Clone(_self.Attributes)
+}
diff --git a/Go/Models/DebugModel.go b/Go/Models/DebugModel.go
new file mode 100644
index 0000000..a533359
--- /dev/null
+++ b/Go/Models/DebugModel.go
@@ -0,0 +1,153 @@
+package Models
+
+import (
+ "AnP/Utils"
+ "encoding/json"
+ "runtime/debug"
+ "strconv"
+)
+
+type DebugStackLineModel struct {
+ Line int
+ Method string
+ File string
+}
+
+func NewDebugStackLineModel(line int, file string, method string) DebugStackLineModel {
+ return DebugStackLineModel{
+ Line: line,
+ File: file,
+ Method: method,
+ }
+}
+
+func NewDebugStackLineFromMatch(match Utils.PatternMatch) DebugStackLineModel {
+
+ line, _ := strconv.ParseInt(match.Groups[4], 10, 32)
+
+ return NewDebugStackLineModel(int(line), match.Groups[3], match.Groups[1])
+}
+
+func (_self *DebugStackLineModel) ToDictionary() map[string]any {
+ return map[string]any{
+ "line": _self.Line,
+ "file": _self.File,
+ "method": _self.Method,
+ }
+}
+
+func DebugStackToDictionaries(lines []DebugStackLineModel) []map[string]any {
+
+ var stack []map[string]any = []map[string]any{}
+
+ for _, line := range lines {
+ stack = append(stack, line.ToDictionary())
+ }
+
+ return stack
+}
+
+func GetTraceStack(i int) []DebugStackLineModel {
+
+ var stack_map []DebugStackLineModel = []DebugStackLineModel{}
+
+ Utils.Patterns.Replace(string(debug.Stack()), Utils.Patterns.RE_TRACE_STACK, func(match Utils.PatternMatch) string {
+
+ stack_map = append(stack_map, NewDebugStackLineFromMatch(match))
+
+ return ""
+ })
+
+ return stack_map[i+2:]
+}
+
+func DebugPrintPointer[T any](item *T) {
+ print("*")
+ if item == nil {
+ print("nil")
+ return
+ }
+ print(*item)
+}
+
+func DebugPrintJSON(item any) {
+ data, error := json.Marshal(item)
+ if error == nil {
+ print(string(data))
+ } else {
+ print(item)
+ }
+}
+
+func DebugPrint(items ...any) {
+
+ var stack DebugStackLineModel = GetTraceStack(1)[0]
+
+ print(">[DEV]>[", stack.Line, "]", stack.File, "(", stack.Method, "): ")
+ for _, item := range items {
+ if item == nil {
+ print("nil")
+ continue
+ }
+
+ switch value := item.(type) {
+ case string:
+ print(value)
+ case *string:
+ DebugPrintPointer(value)
+ case int:
+ print(value)
+ case int8:
+ print(value)
+ case int16:
+ print(value)
+ case int32:
+ print(value)
+ case int64:
+ print(value)
+ case *int:
+ DebugPrintPointer(value)
+ case *int8:
+ DebugPrintPointer(value)
+ case *int16:
+ DebugPrintPointer(value)
+ case *int32:
+ DebugPrintPointer(value)
+ case *int64:
+ DebugPrintPointer(value)
+ case uint:
+ print(value)
+ case uint8:
+ print(value)
+ case uint16:
+ print(value)
+ case uint32:
+ print(value)
+ case uint64:
+ print(value)
+ case *uint:
+ DebugPrintPointer(value)
+ case *uint8:
+ DebugPrintPointer(value)
+ case *uint16:
+ DebugPrintPointer(value)
+ case *uint32:
+ DebugPrintPointer(value)
+ case *uint64:
+ DebugPrintPointer(value)
+ case float32, float64:
+ print(value)
+ case *float32:
+ DebugPrintPointer(value)
+ case *float64:
+ DebugPrintPointer(value)
+ case bool:
+ print(value)
+ default:
+ DebugPrintJSON(value)
+ }
+
+ }
+ print("\n")
+
+}
diff --git a/Go/Models/RoutesModel.go b/Go/Models/RoutesModel.go
new file mode 100644
index 0000000..79ca170
--- /dev/null
+++ b/Go/Models/RoutesModel.go
@@ -0,0 +1,91 @@
+package Models
+
+import (
+ "AnP/Utils"
+ "regexp"
+ "strings"
+)
+
+const (
+ RouteTypeUnknown = iota
+ RouteTypePath
+ RouteTypeView
+ RouteTypeController
+)
+
+type RoutesModel struct {
+ Method string
+ URL *regexp.Regexp
+ View string
+ Path string
+ Ok bool
+ I int
+ Variables []string
+ Permissions []string
+ Type int
+}
+
+func NewRoutesModel(pattern string) RoutesModel {
+
+ var matches Utils.PatternMatch = Utils.Patterns.Match(pattern, Utils.Patterns.RE_ROUTE)
+ var route RoutesModel = RoutesModel{
+ Ok: matches.Ok,
+ }
+
+ if matches.Ok {
+
+ var pattern string
+
+ route.Variables = []string{}
+ pattern = `(?i)^` + Utils.Patterns.Replace(matches.Groups[3], Utils.Patterns.RE_ROUTES_FORMAT, func(matches Utils.PatternMatch) string {
+ if matches.Groups[1] != "" {
+
+ route.Variables = append(route.Variables, matches.Groups[1])
+
+ return `([^\/]+)`
+ }
+ return `\` + matches.Groups[2]
+ })
+
+ if matches.Groups[8] != "" {
+ route.Variables = append(route.Variables, "path")
+ pattern += `(.*)$`
+ } else {
+ pattern += `$`
+ }
+
+ route.Method = strings.ToLower(matches.Groups[2])
+ route.URL = regexp.MustCompile(pattern)
+ // 5 - 6 == Controller - Method
+ route.View = matches.Groups[7]
+ route.Path = matches.Groups[8]
+
+ if matches.Groups[10] != "" {
+ route.Permissions = strings.Split(matches.Groups[10], ",")
+ }
+
+ if route.Path != "" {
+ route.Type = RouteTypePath
+ } else if route.View != "" {
+ route.Type = RouteTypeView
+ }
+
+ }
+
+ return route
+}
+
+func (_self RoutesModel) IsSame(route RoutesModel) bool {
+ return route.Method == _self.Method && route.URL == _self.URL
+}
+
+func (_self RoutesModel) GetVariables(matches Utils.PatternMatch) map[string]any {
+
+ var url_variables map[string]any = map[string]any{}
+
+ for i, key := range _self.Variables {
+ url_variables[key] = matches.Groups[i+1]
+ }
+
+ return url_variables
+}
diff --git a/Go/Tests/HTTPServerTest.go b/Go/Tests/HTTPServerTest.go
new file mode 100644
index 0000000..1c869ec
--- /dev/null
+++ b/Go/Tests/HTTPServerTest.go
@@ -0,0 +1,21 @@
+package Tests
+
+import (
+ "io"
+ "net/http"
+)
+
+type HTTPServerTest struct{}
+
+func NewHTTPServerTest() HTTPServerTest {
+
+ var server HTTPServerTest = HTTPServerTest{}
+
+ http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
+ io.WriteString(writer, "¿PASA? "+request.URL.Path)
+ })
+
+ http.ListenAndServe(":8080", nil)
+
+ return server
+}
diff --git a/Go/Utils/Check.go b/Go/Utils/Check.go
new file mode 100644
index 0000000..4cd6551
--- /dev/null
+++ b/Go/Utils/Check.go
@@ -0,0 +1,28 @@
+package Utils
+
+func IsAnPKey(item any, header string) bool {
+ switch value := item.(type) {
+ case string:
+
+ var l int = len(value)
+
+ return value[:len(header)+1] == header+"_" && value[l-6:] == "_start" || value[l-4:] == "_end"
+ }
+ return false
+}
+
+func IsKey(item any) bool {
+ switch value := item.(type) {
+ case string:
+ return len(value) != 0 && Patterns.RE_KEY.MatchString(value)
+ }
+ return false
+}
+
+func IsPascalKey(item any) bool {
+ switch value := item.(type) {
+ case string:
+ return len(value) != 0 && Patterns.RE_PASCAL_KEY.MatchString(value)
+ }
+ return false
+}
diff --git a/Go/Utils/Patterns.go b/Go/Utils/Patterns.go
new file mode 100644
index 0000000..db07738
--- /dev/null
+++ b/Go/Utils/Patterns.go
@@ -0,0 +1,95 @@
+package Utils
+
+import (
+ "bytes"
+ "regexp"
+)
+
+type PatternMatch struct {
+ String string
+ Groups []string
+ Span []int
+ Ok bool
+}
+
+type PatternsModel struct {
+ RE_PARENT_PATH *regexp.Regexp
+ RE_SLASH *regexp.Regexp
+ RE_KEY *regexp.Regexp
+ RE_PASCAL_KEY *regexp.Regexp
+ RE_STRING_VARIABLES *regexp.Regexp
+ RE_ROUTE *regexp.Regexp
+ RE_ROUTES_FORMAT *regexp.Regexp
+ RE_TRACE_STACK *regexp.Regexp
+ RE_EXTENSION *regexp.Regexp
+ RE_VIEWS_STRING_VARIABLES *regexp.Regexp
+ RE_ATTRIBUTE_BAD_SET_CHARACTERS *regexp.Regexp
+ RE_SPACES *regexp.Regexp
+}
+
+var Patterns PatternsModel = PatternsModel{
+ RE_PARENT_PATH: regexp.MustCompile(`^(.+)[\/\\][^\/\\]+$`),
+ RE_SLASH: regexp.MustCompile(`[\/\\]+`),
+ RE_KEY: regexp.MustCompile(`(?i)^[a-z0-9_]+$`),
+ RE_PASCAL_KEY: regexp.MustCompile(`(?i)^[a-z0-9_\-]+$`),
+ RE_STRING_VARIABLES: regexp.MustCompile(`(?i)\{([a-z0-9_]+)\}`),
+ RE_ROUTE: regexp.MustCompile(`(?i)^(([^\:]+)\:)?([^ ]+) +(([^ \:]+)\:([^ ]+)|([a-z0-9_]+)|([^ ]+))( +([^\s]+))?$`),
+ RE_ROUTES_FORMAT: regexp.MustCompile(`(?i)\{([a-z0-9_]+)\}|([\\\/\-\{\[\]\}\\(\)\*\^\|\!\?\+\.\:\$])`),
+ RE_TRACE_STACK: regexp.MustCompile(`[\n\r](([^\.\(\s]+|\..)+)\([^\n\r]+[\r\n]+\s+([^\:\s]+)\:([0-9]+)`),
+ RE_EXTENSION: regexp.MustCompile(`\.([^\.\/\\]+)$`),
+ RE_VIEWS_STRING_VARIABLES: regexp.MustCompile(`(?i)\{([a-z0-9_]+)\}|\{2}((?:[a-z0-9_]+|\{[a-z0-9_]+\})+)\}{2}`),
+ RE_ATTRIBUTE_BAD_SET_CHARACTERS: regexp.MustCompile(`(?i)[^a-z0-9]+`),
+ RE_SPACES: regexp.MustCompile(` +`),
+}
+
+func (_self *PatternsModel) GetMatches(_string string, groups [][]byte) PatternMatch {
+
+ var binary []byte = []byte(_string)
+ var matches PatternMatch
+
+ if groups != nil {
+
+ matches.String = _string
+ matches.Groups = []string{}
+ matches.Span = []int{bytes.Index(binary, groups[0]), len(groups[0])}
+ matches.Ok = true
+
+ for _, group := range groups {
+ matches.Groups = append(matches.Groups, string(group))
+ }
+
+ }
+
+ return matches
+}
+
+func (_self *PatternsModel) Match(_string string, pattern *regexp.Regexp) PatternMatch {
+ return _self.GetMatches(_string, pattern.FindSubmatch([]byte(_string)))
+}
+
+func (_self *PatternsModel) Replace(_string string, pattern *regexp.Regexp, to any) string {
+
+ var processed string = ""
+ var binary []byte = []byte(_string)
+
+ for {
+
+ var matches PatternMatch = _self.Match(string(binary), pattern)
+
+ if !matches.Ok {
+ break
+ }
+
+ processed += string(binary[:matches.Span[0]])
+ switch value := to.(type) {
+ case func(PatternMatch) string:
+ processed += value(matches)
+ case string:
+ processed += value
+ }
+ binary = binary[matches.Span[0]+matches.Span[1]:]
+
+ }
+
+ return processed + string(binary)
+}
diff --git a/Go/Utils/Utils.go b/Go/Utils/Utils.go
new file mode 100644
index 0000000..a984d89
--- /dev/null
+++ b/Go/Utils/Utils.go
@@ -0,0 +1,345 @@
+package Utils
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "iter"
+ "maps"
+ "slices"
+ "strconv"
+ "strings"
+)
+
+func Get[T any](inputs any, _default *T) T {
+
+ var value T
+
+ if _default != nil {
+ value = *_default
+ }
+
+ switch input_value := inputs.(type) {
+ case T:
+ value = input_value
+ case *T:
+ value = *input_value
+ case []byte:
+ value = any(input_value).(T)
+ }
+
+ return value
+}
+
+func GetPointer[T any](value T) *T {
+ return &value
+}
+
+func Trim(_string string) string {
+ return strings.Trim(_string, " \r\t\n")
+}
+
+func GetStrings(inputs any) []string {
+
+ var _strings []string = []string{}
+
+ switch value := inputs.(type) {
+ case string:
+ _strings = append(_strings, value)
+ case []string:
+ _strings = append(_strings, value...)
+ case []any:
+ for _, subvalue := range value {
+ _strings = append(_strings, GetStrings(subvalue)...)
+ }
+ }
+
+ return _strings
+}
+
+func GetStringsFilled(inputs any) []string {
+
+ var _strings []string = []string{}
+
+ switch value := inputs.(type) {
+ case string:
+ value = Trim(value)
+ if value != "" {
+ _strings = append(_strings, value)
+ }
+ case []string:
+ for _, subvalue := range value {
+ _strings = append(_strings, GetStringsFilled(subvalue)...)
+ }
+ case []any:
+ for _, subvalue := range value {
+ _strings = append(_strings, GetStringsFilled(subvalue)...)
+ }
+ }
+
+ return _strings
+}
+
+func GetKeys(inputs any) []string {
+
+ var keys []string = []string{}
+
+ switch value := inputs.(type) {
+ case string:
+ if IsKey(value) {
+ keys = append(keys, value)
+ }
+ case []string:
+ for _, subvalue := range value {
+ if IsKey(subvalue) && !slices.Contains(keys, subvalue) {
+ keys = append(keys, subvalue)
+ }
+ }
+ case []any:
+ for _, value := range value {
+ for _, subvalue := range GetKeys(value) {
+ if !slices.Contains(keys, subvalue) {
+ keys = append(keys, GetKeys(subvalue)...)
+ }
+ }
+ }
+ }
+
+ return keys
+}
+
+func GetPascalKeys(inputs any) []string {
+
+ var keys []string
+
+ switch value := inputs.(type) {
+ case string:
+ if IsPascalKey(value) {
+ keys = append(keys, value)
+ }
+ case []string:
+ for _, subvalue := range value {
+ if IsPascalKey(subvalue) && !slices.Contains(keys, subvalue) {
+ keys = append(keys, subvalue)
+ }
+ }
+ case []any:
+ for _, value := range value {
+ for _, subvalue := range GetPascalKeys(value) {
+ if !slices.Contains(keys, subvalue) {
+ keys = append(keys, GetPascalKeys(subvalue)...)
+ }
+ }
+ }
+ }
+
+ return keys
+}
+
+func get_value[T any](keys []string, inputs any, _default *T) (T, bool) {
+
+ var response T
+ var ok bool = false
+
+ if _default != nil {
+ response = *_default
+ }
+
+ switch block := inputs.(type) {
+ case map[string]any:
+ for _, key := range keys {
+ value, subok := block[key]
+ if subok {
+ response = Get[T](value, nil)
+ ok = true
+ break
+ }
+ }
+ case []any:
+ for _, subinputs := range block {
+ value, subok := get_value[T](keys, subinputs, nil)
+ if subok {
+ response = value
+ ok = true
+ break
+ }
+ }
+ }
+
+ return response, ok
+}
+
+func GetValue[T any](keys any, inputs any, _default *T) T {
+
+ response, _ := get_value(GetKeys(keys), inputs, _default)
+
+ return response
+}
+
+func AppendInDictionary(dictionary *map[string]any, new_dictionary map[string]any, overwrite bool) {
+ for key, value := range new_dictionary {
+ _, exists := (*dictionary)[key]
+ if overwrite || !exists {
+ (*dictionary)[key] = value
+ }
+ }
+}
+
+func GetDictionary(inputs any, overwrite bool) map[string]any {
+
+ var dictionary map[string]any = map[string]any{}
+
+ switch block := inputs.(type) {
+ case map[string]any:
+ AppendInDictionary(&dictionary, block, overwrite)
+ case []any:
+ for _, subinputs := range block {
+ AppendInDictionary(&dictionary, GetDictionary(subinputs, overwrite), overwrite)
+ }
+ }
+
+ return dictionary
+}
+
+func IterSeqToSlice[T any](iterator iter.Seq[T]) []T {
+
+ var slice []T
+
+ for item := range iterator {
+ slice = append(slice, item)
+ }
+
+ return slice
+}
+
+func GetMapKeys(value any) []string {
+ switch dictionary := value.(type) {
+ case map[string]any:
+ return IterSeqToSlice(maps.Keys(dictionary))
+ }
+ return []string{}
+}
+
+func ToString(data any) string {
+ if data == nil {
+ return "null"
+ }
+ switch value := data.(type) {
+ case string:
+ return value
+ case int:
+ return strconv.Itoa(value)
+ case int8:
+ return strconv.FormatInt(int64(value), 10)
+ case int16:
+ return strconv.FormatInt(int64(value), 10)
+ case int32:
+ return strconv.FormatInt(int64(value), 10)
+ case int64:
+ return strconv.FormatInt(value, 10)
+ case uint:
+ return strconv.FormatUint(uint64(value), 10)
+ case uint8:
+ return strconv.FormatUint(uint64(value), 10)
+ case uint16:
+ return strconv.FormatUint(uint64(value), 10)
+ case uint32:
+ return strconv.FormatUint(uint64(value), 10)
+ case uint64:
+ return strconv.FormatUint(value, 10)
+ case float32:
+ return strconv.FormatFloat(float64(value), 'f', -1, 32)
+ case float64:
+ return strconv.FormatFloat(value, 'f', -1, 64)
+ case bool:
+ return strconv.FormatBool(value)
+ }
+ json, error := json.Marshal(data)
+ if error != nil {
+ return "[object]"
+ }
+ return string(json)
+}
+
+func StringVariables(_string string, variables any, _default *any) string {
+
+ var dictionary map[string]any = GetDictionary(variables, false)
+ var has_default bool = _default != nil
+ var default_processed string = Get[string](_default, nil)
+
+ return Patterns.Replace(_string, Patterns.RE_STRING_VARIABLES, func(match PatternMatch) string {
+ if value, exists := dictionary[match.Groups[1]]; exists {
+ return ToString(value)
+ }
+ if has_default {
+ return default_processed
+ }
+ return match.Groups[0]
+ })
+}
+
+func ToSliceAny[T any](data []T) []any {
+
+ var slice_any []any = []any{}
+
+ for _, value := range data {
+ slice_any = append(slice_any, value)
+ }
+
+ return slice_any
+}
+
+func ToMapAny[T any](data map[string]T) map[string]any {
+
+ var map_any map[string]any = map[string]any{}
+
+ for key, value := range data {
+ map_any[key] = value
+ }
+
+ return map_any
+}
+
+func JSONEncode(data any) string {
+
+ json, _ := json.Marshal(data)
+
+ return string(json)
+}
+
+func GetArray(data any) []any {
+ switch value := data.(type) {
+ case []any:
+ return value
+ }
+ return []any{data}
+}
+
+func GetArgument[T any](arguments []any, i int, _default *T) *T {
+ if arguments != nil && i >= 0 && i < len(arguments) {
+ switch value := arguments[i].(type) {
+ case *T:
+ return value
+ case T:
+ return &value
+ }
+ }
+ return _default
+}
+
+func Base64Encode(_string string) string {
+ return base64.StdEncoding.EncodeToString([]byte(_string))
+}
+
+func Base64Decode(_string string) string {
+
+ data, error := base64.StdEncoding.DecodeString(_string)
+
+ if error != nil {
+ return ""
+ }
+ return string(data)
+}
+
+func VariablesEncode(value any) string {
+ return Base64Encode(JSONEncode(value))
+}
diff --git a/Go/go.mod b/Go/go.mod
new file mode 100644
index 0000000..b6b7ee3
--- /dev/null
+++ b/Go/go.mod
@@ -0,0 +1,3 @@
+module AnP
+
+go 1.24.0
diff --git a/HTML/AnP.wmarkdown.html b/HTML/AnP.wmarkdown.html
new file mode 100644
index 0000000..974e7e0
--- /dev/null
+++ b/HTML/AnP.wmarkdown.html
@@ -0,0 +1,74 @@
+
+
+
+ {title_text}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {contents}
+
+
+
\ No newline at end of file
diff --git a/HTML/AnP.wmarkdown.old.html b/HTML/AnP.wmarkdown.old.html
new file mode 100644
index 0000000..d62be79
--- /dev/null
+++ b/HTML/AnP.wmarkdown.old.html
@@ -0,0 +1,84 @@
+
+
+
+ {title_text}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {contents}
+
+
+
\ No newline at end of file
diff --git a/Public/css/FontAwesome-6.7.2.css b/Public/css/FontAwesome-6.7.2.css
new file mode 100644
index 0000000..e676a82
--- /dev/null
+++ b/Public/css/FontAwesome-6.7.2.css
@@ -0,0 +1,70 @@
+@font-face {
+ font-family : "FA6FB";
+ font-style : normal;
+ font-weight : 400;
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FA6FR";
+ font-style : normal;
+ font-weight : 400;
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FA6FS";
+ font-style : normal;
+ font-weight : 900;
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FA5FB";
+ font-display : block;
+ font-weight : 400;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FA5FS";
+ font-display : block;
+ font-weight : 900;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FA5FR";
+ font-display : block;
+ font-weight : 400;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FAS";
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FAB";
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-brands-400.ttf") format("truetype");
+}
+
+@font-face {
+ font-family : "FAR";
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-regular-400.ttf") format("truetype");
+ unicode-range : U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC;
+}
+
+@font-face {
+ font-family : "FAC";
+ font-display : block;
+ src : url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-v4compatibility.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-v4compatibility.ttf") format("truetype");
+ unicode-range : U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A;
+}
\ No newline at end of file
diff --git a/Public/doc/es/description.w.md b/Public/doc/es/description.w.md
new file mode 100644
index 0000000..b24645d
--- /dev/null
+++ b/Public/doc/es/description.w.md
@@ -0,0 +1,39 @@
+```wmd-options
+language = es
+title_i18n = anp_title_description
+title_text = Descripción - AnP
+since = 20250417
+```
+
+
+
+AnP es un proyecto desarrollado por KyMAN para facilitar el desarrollo de Páginas Web y Aplicaciones Web. En este caso nos encontramos con su versión Lite, es decir, una versión ultraligera en la cual se centró. No es más que un pequeño Framework con las herramientas básicas necesarias que KyMAN vio necesarias para una producción óptima, sencilla y básica. El objetivo de este proyecto es facilitar en lo máximo posible la capacidad de crear servicios web, independientemente de ser Páginas Web con su SEO y otros mecanismos importantes en la difusión de información; así como de Aplicaciones Web y su gestión de los datos e información.
+
+> [[!! IMPORTANTE]] Es de vital importancia que este proyecto, pese a tener un servicio de servidor Web, éste no consta de las herramientas de seguridad pues está pensado para ser implementado detrás de un Proxy, independientemente de que éste sea un Nginx, Apache, IIS, Tomcat, etc. Por lo que el uso directo del servicio de servidor Web de este proyecto sólo sea usado en entorno local específicamente, independientemente de la finalidad o uso del mismo.
+
+La lógica de funcionamiento de este proyecto es muy básico y sencillo. Se basa en un flujo de información cara lo que es cada interactuación con el usuario, independizando lo que viene siendo el lado cliente, del servidor de los datos, siendo ésto último posible cuando hablamos de un motor de datos como los SQL. Su flujo es el siguiente:
+
+```mermaid
+flowchart TD
+
+C[Cliente]
+P([Proxy])
+S[Servidor]
+DB["Bases de datos"]
+
+C -.-> P
+P -.-> S
+C -->|"hace petición a"| S
+S -->|"Consulta a"| DB
+DB -->|"devuelve datos a"| S
+S -->|"procesa y responde a"| C
+S -.-> P
+P -.-> C
+
+```
+
+> [!#] En el esquema vemos el paso intermedio del Proxy con el cual no se enteraría el usuario final pues hace una función intermedia en la petición entre el cliente y el servidor para garantizar en la medida de lo posible una conectividad flexible, múltiple, dinámica y segura.
+
+Esto no quita que el servidor pueda estar conformado en distintas capas tales como pueden ser un Servidor Web y servicios como APIs, WebServices, etc. Incluso puede estar conformado de servicios HTTP y WS (Web Sockets).
+
+
\ No newline at end of file
diff --git a/Public/doc/es/index.w.md b/Public/doc/es/index.w.md
new file mode 100644
index 0000000..93b0eb7
--- /dev/null
+++ b/Public/doc/es/index.w.md
@@ -0,0 +1,12 @@
+```wmd-options
+language = es
+title_i18n = anp_title
+title_text = AnP
+since = 20250417
+```
+
+
+
+[[include description.w.md]]
+
+
\ No newline at end of file
diff --git a/Public/doc/es/targets.w.md b/Public/doc/es/targets.w.md
new file mode 100644
index 0000000..8b6eaeb
--- /dev/null
+++ b/Public/doc/es/targets.w.md
@@ -0,0 +1,39 @@
+```wmd-options
+language = es
+title_i18n = anp_title_targets
+title_text = Objectivos - AnP
+since = 20250417
+```
+
+
+
+AnP es un proyecto desarrollado por KyMAN para facilitar el desarrollo de Páginas Web y Aplicaciones Web. En este caso nos encontramos con su versión Lite, es decir, una versión ultraligera en la cual se centró. No es más que un pequeño Framework con las herramientas básicas necesarias que KyMAN vio necesarias para una producción óptima, sencilla y básica. El objetivo de este proyecto es facilitar en lo máximo posible la capacidad de crear servicios web, independientemente de ser Páginas Web con su SEO y otros mecanismos importantes en la difusión de información; así como de Aplicaciones Web y su gestión de los datos e información.
+
+> [[!! IMPORTANTE]] Es de vital importancia que este proyecto, pese a tener un servicio de servidor Web, éste no consta de las herramientas de seguridad pues está pensado para ser implementado detrás de un Proxy, independientemente de que éste sea un Nginx, Apache, IIS, Tomcat, etc. Por lo que el uso directo del servicio de servidor Web de este proyecto sólo sea usado en entorno local específicamente, independientemente de la finalidad o uso del mismo.
+
+La lógica de funcionamiento de este proyecto es muy básico y sencillo. Se basa en un flujo de información cara lo que es cada interactuación con el usuario, independizando lo que viene siendo el lado cliente, del servidor de los datos, siendo ésto último posible cuando hablamos de un motor de datos como los SQL. Su flujo es el siguiente:
+
+```mermaid
+flowchart TD
+
+C[Cliente]
+P([Proxy])
+S[Servidor]
+DB["Bases de datos"]
+
+C -.-> P
+P -.-> S
+C -->|"hace petición a"| S
+S -->|"Consulta a"| DB
+DB -->|"devuelve datos a"| S
+S -->|"procesa y responde a"| C
+S -.-> P
+P -.-> C
+
+```
+
+> [!#] En el esquema vemos el paso intermedio del Proxy con el cual no se enteraría el usuario final pues hace una función intermedia en la petición entre el cliente y el servidor para garantizar en la medida de lo posible una conectividad flexible, múltiple, dinámica y segura.
+
+Esto no quita que el servidor pueda estar conformado en distintas capas tales como pueden ser un Servidor Web y servicios como APIs, WebServices, etc. Incluso puede estar conformado de servicios HTTP y WS (Web Sockets).
+
+
\ No newline at end of file
diff --git a/Public/ecma/AnPLoader.ecma.js b/Public/ecma/AnPLoader.ecma.js
new file mode 100644
index 0000000..610118e
--- /dev/null
+++ b/Public/ecma/AnPLoader.ecma.js
@@ -0,0 +1,135 @@
+"use strict";
+
+/**
+ * @class
+ * @constructor
+ * @param {!(Object.|Array.)} inputs
+ * @returns {void}
+ * @access public
+ */
+export const AnPLoader = (function(){
+
+ /**
+ * @constructs AnPLoader
+ * @param {!(Object.|Array.)} inputs
+ * @returns {void}
+ * @access private
+ */
+ const AnPLoader = function(inputs){
+
+ /** @type {AnPLoader} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ /** @type {Array.} */
+ const scripts = AnPLoader.get_list_strings(AnPLoader.get_value(["script", "scripts"], inputs)),
+ /** @type {Array.} */
+ styles = AnPLoader.get_list_strings.apply(AnPLoader.get_value(["style", "styles"], inputs));
+
+ };
+
+ constructor();
+
+ };
+
+ /** @type {string} */
+ AnPLoader.domain = /^[^\:]+\:\/{2}[^\/]+\.([lg]ocal|anprm(\.[lg]ocal)?)\/?/i.test(window.location.href) ? "local" : "k3y.pw";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ AnPLoader.is_string = item => typeof item == "string";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ AnPLoader.is_key = item => item && AnPLoader.is_string(item) && /^[a-z0-9_]+$/i.test(item);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ AnPLoader.is_array = item => item instanceof Array;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ AnPLoader.is_dictionary = item => item && item.constructor == Object;
+
+ /**
+ * @param {?any} item
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ AnPLoader.get_array = item => AnPLoader.is_array(item) ? item : [item];
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ AnPLoader.get_keys = keys => AnPLoader.get_array(keys).filter((key, i, array) => AnPLoader.is_key(key) && array.indexOf(key) == i);
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {!(Object.|Array.)} inputs
+ * @param {?any} [_default = null]
+ * @returns {any|null|undefined}
+ * @access public
+ * @static
+ */
+ AnPLoader.get_value = (keys, inputs, _default = null) => {
+ if(AnPLoader.is_array(inputs)){
+ for(let i = 0, l = inputs.length; i < l; i ++){
+
+ /** @type {any|null|undefined} */
+ const value = AnPLoader.get_value(keys, inputs[i], undefined)
+
+ if(value !== undefined)
+ return value;
+
+ };
+ }else if(Check.is_dictionary(inputs)){
+ for(let i = 0, l = (keys = AnPLoader.get_keys(keys)).length; i < l; i ++)
+ if(inputs[keys[i]] !== undefined)
+ return inputs[keys[i]];
+ };
+ return _default;
+ };
+
+ /**
+ * @param {!(string|Array.)} items
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ AnPLoader.get_list_strings = items => {
+ if(items){
+ if(Check.is_string(items))
+ return [items];
+ if(Check.is_array(items))
+ return items.filter(item => Check.is_string(item) || Check.is_array(item)).map(item => Check.is_array(item) ? AnPLoader.get_list_strings(item) : item);
+ };
+ return [];
+ };
+
+ return AnPLoader;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Application/AnP.ecma.js b/Public/ecma/Application/AnP.ecma.js
new file mode 100644
index 0000000..f9d4575
--- /dev/null
+++ b/Public/ecma/Application/AnP.ecma.js
@@ -0,0 +1,291 @@
+"use strict";
+
+import {SettingsManager} from "../Managers/SettingsManager.ecma.js";
+import {I18NManager} from "../Managers/I18NManager.ecma.js";
+import {URLPathDriver} from "../Drivers/URLPathDriver.ecma.js";
+import {ThreadsManager} from "../Managers/ThreadsManager.ecma.js";
+import {Attributes} from "./Attributes.ecma.js";
+import {Components} from "./Components.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Patterns} from "../Utils/Patterns.ecma.js";
+
+/**
+ * @callback anp_preload_callback
+ * @param {?HTMLElement} item
+ * @param {!boolean} asynchronous
+ * @param {!boolean} ok
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_settings_get_callback
+ * @param {!(string|Array.)} keys
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @param {?any} [_default = null]
+ * @returns {any|null}
+ */
+
+/**
+ * @callback anp_start_callback
+ * @param {!boolean} ok
+ * @returns {boolean}
+ */
+
+/**
+ * @callback anp_autostart_callback
+ * @param {!AnP} anp
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {?(Object.|Array.)} inputs
+ * @param {?anp_autostart_callback} [autostart_callback = null]
+ * @returns {void}
+ * @access public
+ */
+export const AnP = (function(){
+
+ /**
+ * @constructs AnP
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @param {?anp_autostart_callback} [autostart_callback = null]
+ * @returns {void}
+ * @access private
+ */
+ const AnP = function(inputs = null, autostart_callback = null){
+
+ /** @type {AnP} */
+ const self = this,
+ /** @type {Array.} */
+ random_chains = [],
+ // http://detectmobilebrowsers.com/
+ /** @type {boolean} */
+ is_mobile = ((a) => (
+ /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))
+ ))(navigator.userAgent || navigator.vendor || window.opera),
+ // https://stackoverflow.com/questions/56393880/how-do-i-detect-dark-mode-using-javascript#answer-57795495
+ /** @type {boolean} */
+ is_dark_mode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
+ /** @type {boolean} */
+ let started = false,
+ /** @type {number} */
+ preload_timeout = 2000,
+ /** @type {string} */
+ chain_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
+ /** @type {number} */
+ chain_length = 11;
+
+ /** @type {SettingsManager} */
+ this.settings = null;
+ /** @type {I18NManager} */
+ this.i18n = null;
+ /** @type {URLPathDriver} */
+ this.request = null;
+ /** @type {ThreadsManager} */
+ this.threads = null;
+ /** @type {Attributes} */
+ this.attributes = this.attr = null;
+ /** @type {Components} */
+ this.components = this.comp = null;
+
+ /** @type {string|null} */
+ this.object_name = null;
+ /** @type {HTMLElement|Document} */
+ this.item_self = document;
+ /** @type {string|null} */
+ this.hash_self = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const set_basics = () => {
+
+ /** @type {anp_settings_get_callback} */
+ const get = self.settings ? self.settings.get : Utils.get_value;
+
+ preload_timeout = get(["preload_timeout", "timeout"], inputs, preload_timeout);
+ chain_alphabet = get(["random_chain_alphabet", "chain_alphabet", "alphabet"], inputs, chain_alphabet);
+ chain_length = get(["random_chain_length", "chain_length", "length"], inputs, chain_length);
+
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const execute_constructor = () => {
+
+ set_basics();
+
+ self.settings = new SettingsManager(self, inputs);
+ self.object_name = self.settings.get("object_name");
+ set_basics();
+ self.i18n = new I18NManager(self);
+ self.request = new URLPathDriver(self);
+ self.threads = new ThreadsManager(self);
+ self.attributes = self.attr = new Attributes(self);
+ self.components = self.comp = new Components(self);
+
+ set_basics();
+
+ self.settings.get("autostart") && self.start();
+
+ }
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+ if(Utils.get_value("debug_mode")){
+ try{
+ execute_constructor();
+ }catch(exception){
+ console.error(exception);
+ };
+ }else
+ execute_constructor();
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ ok && Utils.execute(autostart_callback, self);
+ return ok;
+ };
+
+ if(started)
+ return end(false);
+ started = true;
+
+ Utils.execute_items([
+ "settings", "i18n", "request", "threads", "attributes", "components"
+ ], (key, next) => {
+ self[key].start(ok => {
+ next();
+ });
+ }, () => {
+ end(true);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|HTMLElement)} selector
+ * @param {!anp_preload_callback} callback
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {void}
+ * @access public
+ */
+ this.preload = (selector, callback, inputs = null) => {
+ if(!Check.is_function(callback))
+ return;
+
+ if(Check.is_html_item(selector)){
+ callback(selector, false, true);
+ return;
+ };
+
+ if(Check.is_string(selector)){
+
+ /** @type {HTMLElement|null} */
+ let item;
+
+ try{
+ if(item = self.item_self.querySelector(selector)){
+ callback(item, false, true);
+ return;
+ };
+ }catch(exception){
+ callback(null, false, false);
+ return;
+ };
+
+ /** @type {number} */
+ const date = Date.now(),
+ /** @type {number} */
+ timeout = Utils.get_value(["preload_timeout", "timeout"], inputs, preload_timeout);
+
+ self.threads.add(thread => {
+ if(item = self.item_self.querySelector(selector)){
+ self.threads.remove(thread);
+ callback(item, true, true);
+ }else if(Date.now() - date > timeout){
+ self.threads.remove(thread);
+ callback(null, true, false);
+ };
+ }, {
+ /** @type {boolean} */
+ bucle : true
+ });
+
+ }else
+ callback(null, false, false);
+
+ };
+
+ /**
+ * @returns {string}
+ * @access public
+ */
+ this.random_chain = () => {
+
+ /** @type {string} */
+ let chain;
+ /** @type {number} */
+ const l = chain_alphabet.length;
+
+ do{
+ chain = "";
+ while((chain += chain_alphabet[Math.random() * l >> 0]).length < chain_length);
+ }while(
+ random_chains.includes(chain) ||
+ !Patterns.RE_RIGHT_RANDOM_CHAIN.test(chain) ||
+ document.querySelector("." + chain + ",#" + chain + ",[name=" + chain + "]")
+ );
+ random_chains.push(chain);
+
+ return chain;
+ };
+
+ /**
+ * @param {!string} chain
+ * @returns {void}
+ * @access public
+ */
+ this.remove_random_chain = chain => {
+ random_chains.includes(chain) &&
+ random_chains.splice(random_chains.indexOf(chain), 1);
+ };
+
+ /**
+ * @returns {boolean}
+ * @access public
+ */
+ this.is_mobile = () => is_mobile;
+
+ /**
+ * @returns {boolean}
+ * @access public
+ */
+ this.is_dark_mode = () => is_dark_mode;
+
+ constructor();
+
+ };
+
+ return AnP
+})();
\ No newline at end of file
diff --git a/Public/ecma/Application/Attributes.ecma.js b/Public/ecma/Application/Attributes.ecma.js
new file mode 100644
index 0000000..52c0a4e
--- /dev/null
+++ b/Public/ecma/Application/Attributes.ecma.js
@@ -0,0 +1,260 @@
+"use strict";
+
+import {Check} from "../Utils/Check.ecma.js";
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Patterns} from "../Utils/Patterns.ecma.js";
+import {EventsManager} from "../Managers/EventsManager.ecma.js";
+
+/**
+ * @typedef {import("./AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_attributes_name_callback
+ * @param {!string} name
+ * @param {?string} value
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_attributes_remove_callback
+ * @param {!string} name
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_attributes_load_callback
+ * @param {!HTMLElement} item
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access public
+ */
+export const Attributes = (function(){
+
+ /**
+ * @constructs Attributes
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const Attributes = function(anp){
+
+ /** @type {Attributes} */
+ const self = this,
+ /** @type {Array.} */
+ without_values = [
+ "disabled", "readonly"
+ ],
+ /** @type {Array.} */
+ normals = [
+ "src", "class", "title", "id", "alt", "target", "href", "style", "lang",
+ "type", "name", "placeholder", "min", "max", "value", "step", "disabled",
+ "readonly"
+ ];
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ return end(started ? false : started = true);
+ };
+
+ /**
+ * @param {!(Object.|Array.)} attributes
+ * @param {!Array.} included
+ * @param {!Array.} excluded
+ * @param {!anp_attributes_name_callback} callback
+ * @param {!Object.} parameters
+ * @returns {void}
+ * @access private
+ */
+ const process_fixed = (attributes, included, excluded, callback, parameters) => {
+ if(Check.is_dictionary(attributes)){
+
+ /** @type {boolean} */
+ const has_events_loader = !!parameters.on_load_for_events;
+
+ for(let name in attributes){
+
+ /** @type {any|null} */
+ const value = attributes[name],
+ /** @type {string} */
+ pascal = name.replace(Patterns.RE_ATTRIBUTE_BAD_SET_CHARACTERS, "-").toLowerCase();
+
+ if(
+ (!included.length || included.includes(pascal)) &&
+ (!excluded.length || !excluded.includes(pascal))
+ ){
+ if(pascal.slice(0, 2) == "on" && Check.is_function(value)){
+ parameters.on_load_for_events || (parameters.on_load_for_events = new EventsManager());
+ if(name == "on_load_execute")
+ parameters.on_load_execute = value;
+ else
+ parameters.on_load_for_events.add(item => {
+ item.addEventListener(pascal.replace(Patterns.RE_CLEAN_EVENT_NAME, ""), event => value(item, event));
+ });
+ continue;
+ };
+ callback(
+ ((
+ normals.includes(pascal) ||
+ ["data-", "aria-"].includes(pascal.slice(0, 5))
+ ) ? "" : "data-") + pascal,
+ (
+ without_values.includes(pascal) ? null :
+ Check.is_bool(value) ? value ? "true" : "false" :
+ Check.is_json_object(value) ? Utils.variables_encode(value) :
+ "" + value)
+ );
+ };
+
+ };
+
+ if(!has_events_loader && parameters.on_load_for_events){
+
+ /** @type {string} */
+ const chain = anp.random_chain();
+
+ callback("data-anp-events-loader", chain);
+ anp.preload("[data-anp-events-loader=" + chain + "]", (item, asynchronous, ok) => {
+ anp.remove_random_chain(chain);
+ if(ok){
+ item.removeAttribute("data-anp-events-loader");
+ parameters.on_load_for_events.execute(item);
+ parameters.on_load_execute && parameters.on_load_execute(item);
+ };
+ });
+
+ };
+
+ }else if(Check.is_array(attributes))
+ attributes.forEach(group => {
+ process_fixed(group, included, excluded, callback, parameters);
+ });
+ };
+
+ /**
+ * @param {!(Object.|Array.)} attributes
+ * @param {?(string|Array.)} included
+ * @param {?(string|Array.)} excluded
+ * @param {!anp_attributes_name_callback} callback
+ * @returns {void}
+ * @access private
+ */
+ const process = (attributes, included, excluded, callback) => {
+ process_fixed(attributes, Utils.get_pascal_keys(included), Utils.get_pascal_keys(excluded), callback, {});
+ };
+
+ /**
+ * @param {!(Object.|Array.)} attributes
+ * @param {?(string|Array.)} [included = null]
+ * @param {?(string|Array.)} [excluded = null]
+ * @returns {string}
+ * @access public
+ */
+ this.create = (attributes, included = null, excluded = null) => {
+
+ /** @type {string} */
+ let html = ``;
+
+ process(attributes, included, excluded, (name, value) => {
+ html += ` ` + name + (value === null ? `` : `="` + value + `"`)
+ });
+
+ return html;
+ };
+
+ /**
+ * @param {!(string|HTMLElement)} item
+ * @param {!(Object.|Array.)} attributes
+ * @param {?(string|Array.)} [included = null]
+ * @param {?(string|Array.)} [excluded = null]
+ * @returns {void}
+ * @access public
+ */
+ this.set = (item, attributes, included, excluded) => {
+ anp.preload(item, (item, asynchronous, ok) => {
+ ok && process(attributes, included, excluded, (name, value) => {
+ item.setAttribute(name, value);
+ });
+ });
+ };
+
+ constructor();
+
+ };
+
+ /**
+ * @param {!(string|Array.)} classes
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Attributes.get_classes = classes => Utils.get_pascal_keys(
+ Check.is_array(classes) ? classes :
+ Check.is_string(classes) ? classes.split(Patterns.RE_SPACES) :
+ []);
+
+ /**
+ * @param {!(string|Array.)} classes
+ * @param {?(Object.|Array.)} attributes
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Attributes.join_classes = (...sets) => Utils.get_pascal_keys(
+ sets.reduce((set, new_classes) => set.concat(Attributes.get_classes(new_classes)), [])
+ ).join(" ");
+
+ /**
+ * @param {!(Object.|HTMLElement)} item
+ * @param {!(string|Array.)} names
+ * @returns {Object.|void}
+ * @access public
+ * @static
+ */
+ Attributes.remove = (item, names) => {
+
+ /** @type {anp_attributes_remove_callback|null} */
+ var action = (
+ Check.is_dictionary(item) ? name => {
+ if(item[name] !== undefined)
+ delete item[name];
+ } :
+ Check.is_html_item(item) ? name => {
+ item.hasAttribute(name) &&
+ item.removeAttribute(name)
+ } :
+ null);
+
+ action && Utils.get_pascal_keys(names).forEach(action);
+
+ if(Check.is_dictionary(item))
+ return item;
+ };
+
+ return Attributes;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Application/Components.ecma.js b/Public/ecma/Application/Components.ecma.js
new file mode 100644
index 0000000..0305f3d
--- /dev/null
+++ b/Public/ecma/Application/Components.ecma.js
@@ -0,0 +1,392 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+import {Attributes} from "./Attributes.ecma.js";
+import {LicensesComponent} from "../Components/LicensesComponent.ecma.js";
+import {BaseComponent} from "../Components/BaseComponent.ecma.js";
+
+/**
+ * @typedef {import("./AnP.ecma.js").AnP} AnP
+ * @typedef {import("../Models/ThreadModel.ecma.js").ThreadModel} ThreadModel
+ */
+
+/**
+ * @callback anp_event_callback
+ * @param {!Event} event
+ * @returns {any|null|void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access public
+ */
+export const Components = (function(){
+
+ /**
+ * @constructs Components
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const Components = function(anp){
+
+ /** @type {Components} */
+ const self = this;
+ /** @type {boolean} */
+ let started = false,
+ /** @type {ThreadModel|null} */
+ thread = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ [
+ ["licenses", LicensesComponent],
+ ["base", BaseComponent]
+ ].forEach(([name, Class]) => {
+ if(Class){
+ self[name + "_component"] = new Class(anp);
+ Check.is_function(self[name + "_component"].build) &&
+ (self[name] = self[name + "_component"].build);
+ };
+ });
+
+ };
+
+ /**
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ * @access private
+ */
+ const analyzer = thread => {
+ if(anp.item_self == document)
+ return;
+
+ anp.item_self.querySelectorAll(".image[data-status=unloaded]").forEach(image => {
+ image.setAttribute("data-status", "loading");
+ image.querySelector("img").setAttribute("src", Utils.variables_decode(image.getAttribute("data-sources"))[0]);
+ });
+
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ if(started)
+ return end(false);
+ started = true;
+
+ thread = anp.threads.add(analyzer, {
+ bucle : true,
+ timer : 100
+ });
+
+ Utils.execute_items([
+ "licenses", "base"
+ ], (key, next) => {
+ self[key + "_component"].start(ok => {
+ next();
+ });
+ }, () => {
+ end(true);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {!(Array.)} childs
+ * @returns {Array.|Array.|null>, Array.>|null>>}
+ * @access private
+ */
+ const set_childs = childs => Check.is_string(childs[0]) ? [childs] : childs;
+
+ /**
+ * @param {...Array.|Array.|null>, Array.>|null} items
+ * @returns {string}
+ * @access public
+ */
+ this.set = (...items) => items.reduce((html, [tag, attributes, childs]) => {
+
+ const subitem = (
+ self[tag] && (!attributes || !attributes.built) ? self[tag](...Utils.get_array(attributes)) :
+ `<` + tag + anp.attributes.create(attributes, null, "built") + `>` + (
+ Check.is_array(childs) && childs.length ? self.set(...set_childs(childs)) :
+ Check.is_null_or_undefined(childs) ? "" :
+ "" + childs) + `` + tag + `>`);
+
+ return html + (Check.is_array(subitem) ? self.set(...[subitem]) : subitem);
+ }, ``);
+
+ /**
+ * @param {!(string|HTMLElement)} item
+ * @param {...Array.|Array.|null>, Array.>|null>} items
+ * @returns {string}
+ * @access public
+ */
+ this.set_to = (item, ...items) => {
+ anp.preload(item, (item, asynchronous, ok) => {
+ ok && items.forEach(([tag, attributes, childs]) => {
+ if(self[tag] && (!attributes || !attributes.built)){
+
+ /** @type {string|Array.|HTMLElement|NodeList} */
+ const subitem = self[tag](...Utils.get_array(attributes));
+
+ if(Check.is_string(subitem))
+ item.innerHTML += subitem;
+ else if(Check.is_array(subitem))
+ self.set_to(item, subitem);
+ else if(Check.is_html_items(subitem))
+ subitem.forEach(subnode => item.appendChild(subnode));
+ else if(Check.is_html_item(subitem))
+ item.appendChild(subitem);
+
+ }else{
+
+ /** @type {HTMLElement} */
+ const subitem = item.appendChild(document.createElement(tag));
+
+ anp.attributes.set(subitem, attributes, null, ["built"]);
+ Check.is_array(childs) && childs.length && self.set_to(subitem, ...set_childs(childs));
+
+ };
+ });
+ });
+ };
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @returns {Array.}
+ * @access public
+ */
+ this.image = inputs => {
+
+ /** @type {Object.} */
+ const attributes = Utils.get_dictionary(Check.is_array_string(inputs) ? {sources : inputs} : inputs),
+ /** @type {Array.} */
+ source_keys = ["images", "sources", "image", "source", "src"],
+ /** @type {Array.} */
+ images = Utils.get_strings(Utils.get_value(source_keys, attributes));
+
+ Attributes.remove(attributes, source_keys.concat([
+ "class", "classes", "status", "data_status", "data_i", "data_sources"
+ ]));
+
+ return ["span", {
+ ...attributes,
+ class : Attributes.join_classes("image", Utils.get_value(["class", "classes"], attributes)),
+ data_status : "unloaded",
+ data_sources : images,
+ data_i : 0
+ }, [
+ ["img", {
+ // on_load_execute : item => {
+ // item.setAttribute("src", images[0]);
+ // },
+ onload : (item, event) => {
+ item.parentNode.setAttribute("data-status", "loaded");
+ item.parentNode.querySelector("span").style.backgroundImage = "url('" + item.src + "')";
+ },
+ onerror : (item, event) => {
+
+ /** @type {Array.} */
+ const images = Utils.variables_decode(item.parentNode.getAttribute("data-sources")),
+ /** @type {number} */
+ i = Number(item.parentNode.getAttribute("data-i")) + 1;
+
+ if(i < images.length){
+ item.parentNode.setAttribute("data-i", i);
+ item.setAttribute("src", images[i]);
+ }else
+ item.parentNode.setAttribute("data-status", "error");
+
+ },
+ ...(attributes.title ? {alt : attributes.title} : {})
+ }],
+ ["span"]
+ ]];
+ };
+
+ /**
+ * @param {!string} name
+ * @param {!string} [tag = "span"]
+ * @param {?(Object.|Array.)} [attributes = null]
+ * @returns {Array.}
+ * @access public
+ */
+ this.icon = (name, tag = "span", attributes = null) => {
+
+ /** @type {Array.|string|null} */
+ const childs = Utils.get_value(["content", "child", "childs"], attributes, null);
+
+ Attributes.remove(attributes = Utils.get_dictionary(attributes), [
+ ["content", "child", "childs"]
+ ]);
+ attributes.data_icon = name;
+
+ return [tag, attributes, childs];
+ };
+
+ /**
+ * @param {?string} base
+ * @param {?(Object.|Array.)} attributes
+ * @returns {Array.}
+ * @access private
+ */
+ const process_title = (base, attributes) => [
+ anp.i18n.get([base].concat(Utils.get_strings(Utils.get_value([
+ "title", "text", "texts", "default_text", "default_texts"
+ ], attributes, []))), attributes),
+ (
+ !Check.is_null_or_undefined(Utils.get_dictionary(attributes).title) ||
+ Utils.get_value(["with_title", "has_title"], attributes, false)
+ )
+ ];
+
+ /**
+ * @param {!string} name
+ * @param {!string} [tag = "span"]
+ * @param {?(Object.|Array.)} [attributes = null]
+ * @returns {Array.}
+ * @access public
+ */
+ this.i18n = (name, tag = "span", attributes = null) => {
+
+ /** @type {[string, boolean]} */
+ const [text, has_title] = process_title(name, attributes);
+
+ Attributes.remove(attributes = Utils.get_dictionary(attributes), [
+ "text", "texts", "default_text", "default_texts", "with_title", "has_title"
+ ]);
+ attributes.data_i18n = name;
+ has_title && (attributes.title = text);
+
+ return [tag, attributes, text];
+ };
+
+ /**
+ * @param {!string} name
+ * @param {?(anp_event_callback|Object.|Array.)} [attributes = null]
+ * @returns {Array.}
+ */
+ this.button = (name, attributes = null) => {
+
+ /** @type {[string, boolean]} */
+ const [text, has_title] = process_title(name, {with_title : true, ...(
+ Check.is_function(attributes) ? attributes = {onclick : attributes} :
+ attributes)}),
+ /** @type {Array.|string>} */
+ childs = [
+ self.icon(Utils.get_value(["icon", "data_icon"], attributes, name)),
+ self.i18n(name)
+ ],
+ /** @type {Array.|string|null} */
+ content = Utils.get_value(["child", "childs", "content"], attributes, null);
+
+ content && childs.push(content);
+
+ Attributes.remove(attributes = Utils.get_dictionary(attributes), [
+ "text", "texts", "default_text", "default_texts", "with_title", "has_title",
+ "icon", "data_icon", "child", "childs", "content"
+ ]);
+ attributes.built = true;
+ attributes.type || (attributes.type = "button");
+
+ if(has_title){
+ attributes.title = text;
+ attributes.data_i18n = name;
+ attributes.data_i18n_without = true;
+ };
+
+ return ["button", {
+ ...attributes,
+ }, childs];
+ };
+
+ /**
+ * @param {!Array.|string>} items
+ * @param {?(Object.|Array.)} [attributes = null]
+ * @param {!string} [tag = "div"]
+ * @returns {Array.}
+ * @access public
+ */
+ this.group = (items, attributes = null, tag = "div") => {
+
+ /** @type {string} */
+ const classes = Attributes.join_classes("group", Utils.get_value(["class", "classes"], attributes));
+
+ Attributes.remove(attributes = Utils.get_dictionary(attributes), [
+ "class", "classes"
+ ]);
+ attributes.class = classes;
+
+ return [tag, attributes, items];
+ };
+
+ /**
+ * @param {!Array.|string>} buttons
+ * @param {?(Object.|Array.)} [attributes = null]
+ * @param {!string} [tag = "div"]
+ * @returns {Array.}
+ * @access public
+ */
+ this.buttons = (buttons, attributes = null, tag = "div") => self.group(buttons, Utils.get_dictionary([attributes, {
+ class : Attributes.join_classes("buttons", Utils.get_value(["class", "classes"], attributes))
+ }]), tag);
+
+ /**
+ * @param {?(Object.)} [attributes = null]
+ * @param {?(Object.)} [own_attributes = null]
+ * @returns {Array.}
+ * @access public
+ */
+ this.input = (attributes = null, own_attributes = null) => ["input", {
+ ...Utils.get_dictionary(attributes),
+ ...Utils.get_dictionary(own_attributes),
+ built : true
+ }];
+
+ /**
+ * @param {?(Object.)} [attributes = null]
+ * @returns {Array.}
+ * @access public
+ * @returns
+ */
+ this.number = (attributes = null) => {
+
+ /** @type {string} */
+ const classes = Attributes.join_classes("input-field input-number", Utils.get_value(["class", "classes"], attributes));
+
+ Attributes.remove(attributes = Utils.get_dictionary(attributes), ["class", "classes"]);
+
+ return ["span", {class : classes}, [
+ ["input", {
+ ...attributes,
+ type : "number"
+ }]
+ ]];
+ };
+
+ constructor();
+
+ };
+
+ return Components;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/BaseComponent.ecma.js b/Public/ecma/Components/BaseComponent.ecma.js
new file mode 100644
index 0000000..39a1397
--- /dev/null
+++ b/Public/ecma/Components/BaseComponent.ecma.js
@@ -0,0 +1,452 @@
+"use strict";
+
+import {Attributes} from "../Application/Attributes.ecma.js";
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ * @typedef {import("../Models/ThreadModel.ecma.js").ThreadModel} ThreadModel
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access public
+ */
+export const BaseComponent = (function(){
+
+ /**
+ * @constructs BaseComponent
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const BaseComponent = function(anp){
+
+ /** @type {BaseComponent} */
+ const self = this,
+ /** @type {Array.>>} */
+ identifiers = [];
+ /** @type {boolean} */
+ let started = false,
+ /** @type {ThreadModel|null} */
+ thread = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ * @access private
+ */
+ const analyzer = thread => {
+
+ /** @type {Array.} */
+ const for_delete = [];
+
+ identifiers.forEach((cache, i) => {
+ if(!cache.item && !(cache.item = document.querySelector("#" + cache.id))){
+ for_delete.push(i);
+ return;
+ };
+
+ /** @type {number} */
+ const zoom = Number(cache.item.getAttribute("data-zoom")) / 100;
+
+ if(
+ cache.x != cache.item.offsetWidth ||
+ cache.y != cache.item.offsetHeight ||
+ cache.zoom != zoom
+ ){
+ cache.x = cache.item.offsetWidth;
+ cache.y = cache.item.offsetHeight;
+ cache.zoom = zoom;
+
+ /** @type {number} */
+ const cells = Number(cache.item.getAttribute("data-cells")),
+ /** @type {number} */
+ minimum_size = Number(cache.item.getAttribute("data-minimum-size")),
+ /** @type {number} */
+ size = zoom * (cache.x < cache.y ? cache.x : cache.y) / cells;
+
+ cache.item.style.fontSize = (size > minimum_size ? size : minimum_size) + "px";
+
+ cache.item.querySelectorAll("[data-maximum-zoom]").forEach(item => {
+
+ /** @type {number} */
+ const maximum_zoom = Number(item.getAttribute("data-maximum-zoom")) / 100,
+ /** @type {number} */
+ maximum_size = maximum_zoom * (cache.x < cache.y ? cache.x : cache.y) / cells;
+
+ item.style.fontSize = size > maximum_size ? maximum_size + "px" : "inherit";
+
+ });
+
+ };
+
+ });
+
+ if(for_delete.length){
+ for_delete.reverse();
+ for_delete.forEach(i => {
+ identifiers.splice(i, 1);
+ });
+ };
+
+ };
+
+ /**
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {Object.}
+ * @access private
+ */
+ const get_attributes = (inputs = null) => {
+
+ /** @type {string} */
+ const id = anp.random_chain(),
+ /** @type {Object.} */
+ attributes = Utils.get_dictionary(inputs),
+ /** @type {anp_attributes_load_callback|null} */
+ on_load_execute = attributes.on_load_execute;
+
+ Attributes.remove(attributes, [
+ "class", "classes", "id", "data_hash", "hash", "cells", "minimum_size",
+ "zoom", "data_cells", "data_minimum_size", "data_zoom", "on_load_execute",
+ "data_dark_mode", "data_mobile", "dark_mode", "mobile"
+ ]);
+
+ return {
+ ...attributes,
+ on_load_execute : item => {
+ identifiers.push({id : id});
+ Utils.execute(on_load_execute, item);
+ },
+ class : Attributes.join_classes(Utils.get_value(["class", "classes"], inputs, []), ["anp", id]),
+ id : id,
+ data_hash : id,
+ data_cells : anp.settings.get(["base_cells", "data_cells", "cells"], inputs, 40),
+ data_minimum_size : anp.settings.get(["base_minimum_size", "data_minimum_size", "minimum_size"], inputs, 12),
+ data_zoom : anp.settings.get(["base_zoom", "data_zoom", "zoom"], inputs, 100),
+ data_gui_mode : anp.settings.get(["base_gui_mode", "data_gui_mode", "gui_mode"], inputs, "default"),
+ data_dark_mode : anp.is_dark_mode(),
+ data_mobile : anp.is_mobile()
+ };
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ if(started)
+ return end(false);
+ started = true;
+
+ /** @type {string|HTMLElement|null} */
+ const position = anp.settings.get("position");
+
+ thread = anp.threads.add(analyzer, {
+ timer : 100,
+ bucle : true
+ });
+
+ if(position)
+ anp.preload(position, (position, asyncrhonous, ok) => {
+ if(ok){
+
+ /** @type {Object.} */
+ const attributes = {
+ on_load_execute : item => {
+ anp.item_self = item;
+ anp.hash_self = item.getAttribute("id");
+ end(true);
+ }
+ };
+
+ ["name", "git", "link", "since", "version"].forEach(key => {
+
+ const value = anp.settings.get("application_" + key);
+
+ Check.is_null_or_undefined(value) ||
+ (attributes["data_" + key] = value);
+
+ });
+
+ if(position.classList.contains("anp")){
+
+ /** @type {HTMLElement} */
+ const footer = position.querySelector("footer"),
+ /** @type {HTMLFieldSetElement} */
+ gui_controls = footer.insertBefore(document.createElement("fieldset"), footer.childNodes[0]);
+
+ anp.components.set_to(gui_controls, ...self.build_view_menu());
+ gui_controls.setAttribute("class", "gui-controls");
+ anp.attributes.set(position, get_attributes(attributes));
+
+ }else{
+ try{
+ position.innerHTML = self.build(attributes);
+ }catch(exception){
+ console.error(exception);
+ };
+ };
+ }else
+ end(false);
+ });
+ else
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @returns {string}
+ * @access public
+ */
+ this.build = inputs => {
+
+ /** @type {Array.} */
+ const licenses = anp.settings.get(["application_licenses", "application_license", "licenses", "license"], inputs, []),
+ /** @type {Object.} */
+ attributes = get_attributes(inputs);
+
+ return anp.components.set(["div", attributes, [
+ ["header", null, [
+ ["h1", {class : "logo", title : "AnP"}, [
+ ["a", {href : "https://anp.k3y.pw/", target : "_blank"}, [
+ ["image", {sources : "/images/AnP.png", title : "AnP"}],
+ ["span", {class : "text"}, "AnP"]
+ ]]
+ ]]
+ ]],
+ ["main"],
+ ["footer", {
+ "data_maximum_zoom" : anp.settings.get(["base_footer_maximum_zoom", "footer_maximum_zoom"], inputs, 100)
+ }, [].concat(
+ [["fieldset", {class : "gui-controls"}, self.build_view_menu()]],
+ (licenses ? [["licenses", licenses]] : [])
+ )]
+ ]]);
+ };
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.build_view_menu = (inputs = null) => [
+ ["legend", {data_i18n : "gui_controls"}, anp.i18n.get("gui_controls")],
+ ["button", ["less_zoom", set_less_zoom]],
+ ["number", {
+ name : "zoom",
+ min : anp.settings.get("zoom_minimum", inputs, 1),
+ max : anp.settings.get("zoom_maximum", inputs, 200),
+ value : anp.settings.get(["default_zoom", "base_zoom", "zoom"], inputs, 100),
+ step : anp.settings.get("zoom_step", inputs, 1),
+ onchange : change_zoom
+ }],
+ ["button", ["reset_zoom", reset_zoom]],
+ ["button", ["zoom_mode", {
+ onclick : change_zoom_mode,
+ data_modes : anp.settings.get("zoom_modes", inputs, [50, 75, 100, 125, 150])
+ }]],
+ ["button", ["more_zoom", set_more_zoom]],
+ ["button", ["gui_mode", change_gui_mode]],
+ ["button", ["more_options", {
+ onclick : show_more_options,
+ disabled : true
+ }]]
+ ];
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {!number} value
+ * @returns {void}
+ * @access public
+ */
+ this.change_zoom = (item, value) => {
+
+ /** @type {HTMLInputElement} */
+ const number_field = (item = BaseComponent.get(item)).querySelector("footer .gui-controls [type=number]");
+
+ item.setAttribute("data-zoom", value);
+ Number(number_field.value) != value && (number_field.value = value);
+
+ };
+
+ /**
+ * @param {!HTMLElement} button
+ * @param {!number} value
+ * @returns {void}
+ * @access private
+ */
+ const set_zoom = (button, value) => {
+ if(Check.is_number(value)){
+
+ /** @type {HTMLInputElement} */
+ const number_field = button.parentNode.querySelector("[type=number]");
+
+ if(value){
+
+ value += Number(number_field.value) || 0;
+
+ if(value < 0){
+
+ /** @type {number} */
+ const minimum = Number(number_field.getAttribute("min"));
+
+ !isNaN(minimum) && minimum > value && (value = minimum);
+
+ }else{
+
+ /** @type {number} */
+ const maximum = Number(number_field.getAttribute("max"));
+
+ !isNaN(maximum) && maximum < value && (value = maximum);
+
+ };
+
+ number_field.value = value;
+
+ }else
+ number_field.value = value = 100;
+
+ self.change_zoom(button, value);
+
+ };
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const set_less_zoom = (item, event = null) => {
+ set_zoom(item, -BaseComponent.get_input_gui_step(item));
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const change_zoom_mode = (item, event = null) => {
+
+ /** @type {number} */
+ const current_zoom = Number(item.parentNode.querySelector("[type=number]").value),
+ /** @type {Array.} */
+ modes = Utils.variables_decode(item.getAttribute("data-modes"));
+
+ modes.some((value, i) => {
+ if(current_zoom < value){
+ self.change_zoom(item, value);
+ return true;
+ };
+ }) || self.change_zoom(item, modes[0]);
+
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const reset_zoom = (item, event = null) => {
+ set_zoom(item, 0);
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const set_more_zoom = (item, event = null) => {
+ set_zoom(item, BaseComponent.get_input_gui_step(item));
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const change_gui_mode = (item, event = null) => {
+ (item = BaseComponent.get(item)).setAttribute("data-gui-mode", {
+ "default" : "dark",
+ "dark" : "light",
+ "light" : "default"
+ }[item.getAttribute("data-gui-mode")] || "default");
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const show_more_options = (item, event = null) => {
+ console.log("PASA");
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {?Event} [event = null]
+ * @returns {void}
+ * @access private
+ */
+ const change_zoom = (item, event = null) => {
+ self.change_zoom(item, Number(item.value));
+ };
+
+ constructor();
+
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @returns {HTMLElement}
+ * @access public
+ * @static
+ */
+ BaseComponent.get = item => {
+
+ if(item)
+ while(true){
+ if(!item.classList)
+ return null;
+ if(item.classList.contains("anp") || !(item = item.parentNode))
+ break;
+ };
+
+ return item || null;
+ };
+
+ /**
+ * @param {!HTMLElement} item
+ * @returns {number}
+ * @access public
+ * @static
+ */
+ BaseComponent.get_input_gui_step = item => Number(item.parentNode.querySelector("[type=number]").getAttribute("step"));
+
+ return BaseComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/LicensesComponent.ecma.js b/Public/ecma/Components/LicensesComponent.ecma.js
new file mode 100644
index 0000000..d864044
--- /dev/null
+++ b/Public/ecma/Components/LicensesComponent.ecma.js
@@ -0,0 +1,161 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+export const LicensesComponent = (function(){
+
+ /**
+ * @constructs LicensesComponent
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const LicensesComponent = function(anp){
+
+ /** @type {LicensesComponent} */
+ const self = this;
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ return end(started ? false : started = true);
+ };
+
+ /**
+ * @param {!...Array.} [licenses]
+ * @returns {Array.}
+ * @access public
+ */
+ this.build = (...licenses) => ["span", {
+ class : "licenses"
+ }, licenses.map(([name, ...inputs]) => (
+ Check.is_function(self[name]) ? self[name](...inputs) :
+ []))];
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.gplv3 = () => ["a", {
+ class : "license gpl-v3",
+ href : "https://www.gnu.org/licenses/gpl-3.0.txt",
+ target : "_blank",
+ title : "GPLv3"
+ }, [
+ ["span", {class : "text"}, "GPLv3"],
+ ["image", {
+ sources : "https://www.gnu.org/graphics/gplv3-88x31.png",
+ alt : "GPLv3"
+ }]
+ ]];
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.cc_by_nc_sa_4 = () => {
+
+ /** @type {string} */
+ const text = anp.i18n.get([
+ "Creative Commons Attribution-NonCommertial-ShareAlike 4.0 International (CC-SA-NC-BY 4.0)",
+ "cc_by_nc_sa_4"
+ ]);
+
+ return ["a", {
+ class : "license cc-by-nc-sa-4",
+ href : "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ target : "_blank",
+ data_i18n : "cc_by_nc_sa_4",
+ data_i18n_without : true,
+ title : text
+ }, [
+ ["span", {class : "text"}, text],
+ ["image", {
+ sources : "https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png",
+ data_i18n : "cc_by_nc_sa_4",
+ data_i18n_without : true,
+ alt : text
+ }]
+ ]];
+ };
+
+ /**
+ * @param {?(number|string|Array.)} [year = null]
+ * @param {?(string|Array.)} [authors = null]
+ * @param {!boolean} [all_rights_reserved = false]
+ * @returns {Array.}
+ * @access public
+ */
+ this.copyright = (year = null, authors = null, all_rights_reserved = false) => {
+
+ /** @type {Object.} */
+ const variables = {
+ year : Check.is_array(year) ? (
+ year.length == 1 ? year[0] :
+ year.length == 2 ? year.join("-") :
+ year.length ? year.slice(0, year.length - 1).join(", ") + " & " + year.slice(-1)[0] :
+ "") : year || "",
+ authors : Check.is_array(authors) ? (
+ authors.length == 1 ? authors[0] :
+ authors.length == 2 ? authors.join(" & ") :
+ authors.length ? authors.slice(0, authors.length - 1).join(", ") + " & " + authors.slice(-1)[0] :
+ "") : authors || "",
+ all_rights_reserved : all_rights_reserved ? anp.i18n.get([
+ "All rights reserved.",
+ "all_rights_reserved"
+ ]) : ""
+ },
+ /** @type {string} */
+ text = anp.i18n.get([
+ "© Copyright{year}{authors}{all_rights_reserved}",
+ "copyright"
+ ], {
+ year : variables.year = variables.year ? " " + variables.year + "." : "",
+ authors : variables.authors = variables.authors ? " " + variables.authors + "." : "",
+ all_rights_reserved : variables.all_rights_reserved = variables.all_rights_reserved ? (variables.year || variables.authors ? "" : ". ") + variables.all_rights_reserved : ""
+ });
+
+ return ["span", {
+ class : "license copyright",
+ data_i18n : "copyriht",
+ data_i18n_variables : variables,
+ title : text
+ }, text];
+ };
+
+ constructor();
+
+ };
+
+ return LicensesComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Drivers/URLPathDriver.ecma.js b/Public/ecma/Drivers/URLPathDriver.ecma.js
new file mode 100644
index 0000000..62aae1a
--- /dev/null
+++ b/Public/ecma/Drivers/URLPathDriver.ecma.js
@@ -0,0 +1,199 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_url_path_driver_default_callback
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_url_path_driver_callback
+ * @param {?string} response
+ * @param {!number} status
+ * @param {!number} state
+ * @param {!boolean} ok
+ * @param {!string} message
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_url_path_driver_ajax_end_callback
+ * @param {!string} message
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_url_path_driver_load_json_each_callback
+ * @param {?any} item
+ * @param {!anp_url_path_driver_default_callback} calback
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access public
+ */
+export const URLPathDriver = (function(){
+
+ /**
+ * @constructs URLPathDriver
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const URLPathDriver = function(anp){
+
+ /** @type {URLPathDriver} */
+ const self = this;
+ /** @type {boolean} */
+ let started = false,
+ /** @type {boolean} */
+ default_timeout = false;
+
+ /**
+ * @returns {void}
+ * @access public
+ */
+ this.set_basics = () => {
+
+ default_timeout = anp.settings.get(["ajax_timeout", "timeout"], null, default_timeout);
+
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ self.set_basics();
+
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ return end(started ? false : started = true);
+ };
+
+ /**
+ * @param {!XMLHttpRequest} ajax
+ * @param {!anp_url_path_driver_callback} callback
+ * @param {?(Object.|Array.)} inputs
+ * @param {?(string|Object.|Array.)} [data = null]
+ * @returns {XMLHttpRequest}
+ * @access private
+ */
+ const execute = (ajax, callback, inputs, data = null) => {
+
+ let ended = false;
+ const date = Date.now(),
+ timeout = Utils.get_value(["ajax_timeout", "timeout"], inputs, default_timeout),
+ end = message => {
+ !ended && (ended = true) && Utils.execute(
+ callback, ajax.responseText, ajax.status, ajax.readyState, message == "OK", message
+ );
+ };
+
+ ajax.timeout = timeout;
+ ajax.onreadystatechange = () => {
+ if(ended)
+ return;
+ if(ajax.readyState == 4)
+ end((ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status) ? "OK" : "HTTP_ERROR");
+ else if(timeout && Date.now() - date > timeout)
+ end("FORCED_TIMEOUT");
+ };
+
+ ajax.onabort = () => end("ABORTED");
+ ajax.onerror = () => end("ERROR");
+ ajax.ontimeout = () => end("TIMEOUT");
+
+ ajax.send(data);
+
+ return ajax;
+ };
+
+ /**
+ * @param {!string} url
+ * @param {!anp_url_path_driver_callback} callback
+ * @param {?(object.|Array.)} [inputs = null]
+ * @returns {XMLHttpRequest}
+ * @access public
+ */
+ this.get = (url, callback = null, inputs = null) => {
+
+ /** @type {XMLHttpRequest} */
+ const ajax = new XMLHttpRequest();
+
+ ajax.open("get", url, true);
+
+ return execute(ajax, callback, inputs);
+ };
+
+ /**
+ * @param {!(string|Object.|Array.)} inputs
+ * @param {!anp_url_path_driver_default_callback} each_callback
+ * @param {?anp_url_path_driver_default_callback} [end_callback = null]
+ * @param {!boolean} [only_dictionaries = true]
+ * @returns {void}
+ * @access public
+ */
+ this.load_json = (inputs, each_callback, end_callback = null, only_dictionaries = true) => {
+ if(Check.is_string(inputs)){
+
+ /** @type {Object.|Array.|null} */
+ let json;
+
+ try{
+ json = JSON.parse(inputs);
+ }catch(exception){};
+
+ if(json)
+ self.load_json(json, each_callback, end_callback, only_dictionaries);
+ else
+ self.get(inputs, (response, status, state, ok, message) => {
+ try{
+ json = JSON.parse(response);
+ }catch(exception){};
+ self.load_json(json, each_callback, end_callback, only_dictionaries);
+ });
+
+ }else if(Check.is_array(inputs)){
+ if(only_dictionaries)
+ Utils.execute_items(inputs, (item, callback) => {
+ self.load_json(item, each_callback, callback, only_dictionaries);
+ }, end_callback);
+ else
+ Utils.execute(each_callback, inputs, end_callback);
+ }else if(Check.is_dictionary(inputs))
+ Utils.execute(each_callback, inputs, end_callback);
+ else
+ Utils.execute(end_callback);
+ };
+
+ constructor();
+
+ };
+
+ return URLPathDriver;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/EventsManager.ecma.js b/Public/ecma/Managers/EventsManager.ecma.js
new file mode 100644
index 0000000..36f8ae6
--- /dev/null
+++ b/Public/ecma/Managers/EventsManager.ecma.js
@@ -0,0 +1,73 @@
+"use string";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+
+/**
+ * @callback anp_events_manager_callback
+ * @param {...any} [parameters]
+ * @returns {any|null}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {void}
+ * @access public
+ */
+export const EventsManager = (function(){
+
+ /**
+ * @constructs EventsManager
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {void}
+ * @access private
+ */
+ const EventsManager = function(inputs = null){
+
+ /** @type {Array.} */
+ const events = [];
+
+ /** @type {boolean} */
+ this.autoexecute = Utils.get_value("autoexecute", inputs, false);
+
+ /**
+ * @param {...any} [parameters]
+ * @returns {Array.}
+ * @access public
+ */
+ this.execute = (...parameters) => events.map(callback => callback ? callback(...parameters) : null);
+
+ /**
+ * @param {!anp_events_manager_callback} callback
+ * @returns {number}
+ * @access public
+ */
+ this.add = callback => {
+
+ /** @type {number} */
+ let i = -1;
+ /** @type {number} */
+ const l = events.length;
+
+ while(++ i < l)
+ if(!events[i])
+ break;
+ events[i] = callback;
+
+ return i;
+ };
+
+ /**
+ * @param {!number} i
+ * @returns {void}
+ * @access public
+ */
+ this.remove = i => {
+ events[i] && (events[i] = null);
+ };
+
+ };
+
+ return EventsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/I18NManager.ecma.js b/Public/ecma/Managers/I18NManager.ecma.js
new file mode 100644
index 0000000..ea6debc
--- /dev/null
+++ b/Public/ecma/Managers/I18NManager.ecma.js
@@ -0,0 +1,152 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_i18n_settings_default_callback
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @param {?(Object.|Array.)} inputs
+ * @returns {void}
+ * @access public
+ */
+export const I18NManager = (function(){
+
+ /**
+ * @constructs I18NManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const I18NManager = function(anp){
+
+ /** @type {I18NManager} */
+ const self = this,
+ /** @type {Object.} */
+ sentences = {};
+ /** @type {boolean} */
+ let started = false,
+ /** @type {string} */
+ default_language = "english",
+ /** @type {string} */
+ selected_language = "english";
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ if(started)
+ return end(false);
+ started = true;
+
+ Utils.execute_items([
+ "default_i18n_files", "default_i18n", "i18n_files", "i18n"
+ ], (key, next) => {
+ self.add(anp.settings.get(key), next, true);
+ }, () => {
+ end(true);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|Object.|Array.)} inputs
+ * @param {?anp_i18n_settings_default_callback} [callback = null]
+ * @param {!boolean} [overwrite = false]
+ * @returns {void}
+ * @access public
+ */
+ this.add = (inputs, callback = null, overwrite = false) => {
+ anp.request.load_json(inputs, (data, subcallback) => {
+ for(const language in data)
+ if(Check.is_dictionary(data[language])){
+ sentences[language] || (sentences[language] = {});
+ for(const key in data[language])
+ !Check.is_key_mark(key) &&
+ (overwrite || sentences[language][key] === undefined) &&
+ (sentences[language][key] = data[language][key]);
+ };
+ Utils.execute(subcallback);
+ }, callback);
+ };
+
+ /**
+ * @param {!(string|Array.)} texts
+ * @param {?(string|Array.)} languages
+ * @returns {string|Array.|null}
+ * @access private
+ */
+ const get_sentence = (texts, languages) => {
+
+ /** @type {Array.} */
+ const keys = Utils.get_keys(texts);
+
+ if(keys.length){
+
+ /** @type {number} */
+ const l = (languages = Utils.get_keys(Utils.get_keys(languages).concat(
+ [selected_language, default_language],
+ Object.keys(sentences)
+ ))).length;
+
+ for(let i = 0; i < l; i ++)
+ if(sentences[languages[i]])
+ for(let j = 0, m = keys.length; j < m; j ++)
+ if(sentences[languages[i]][keys[j]] !== undefined)
+ return sentences[languages[i]][keys[j]];
+ };
+ return Utils.get_strings(texts)[0];
+ };
+
+ /**
+ * @param {!(string|Array.)} texts
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @param {?(string|Array.)} [languages = null]
+ * @returns {string|null}
+ * @access public
+ */
+ this.get = (texts, inputs = null, languages = null) => {
+
+ /** @type {string|Array.|null} */
+ const fragments = get_sentence(texts, languages),
+ /** @type {string|null} */
+ sentence = Check.is_array(fragments) ? fragments.join("") : fragments;
+
+ return inputs ? Utils.string_variables(sentence, inputs) : sentence;
+ };
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ I18NManager.DEFAULT_SETTINGS = {};
+
+ return I18NManager;
+})()
\ No newline at end of file
diff --git a/Public/ecma/Managers/SettingsManager.ecma.js b/Public/ecma/Managers/SettingsManager.ecma.js
new file mode 100644
index 0000000..5b9ee38
--- /dev/null
+++ b/Public/ecma/Managers/SettingsManager.ecma.js
@@ -0,0 +1,151 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {Check} from "../Utils/Check.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_settings_managers_default_callback
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @param {?(Object.|Array.)} inputs
+ * @returns {void}
+ * @access public
+ */
+export const SettingsManager = (function(){
+
+ /**
+ * @constructs SettingsManager
+ * @param {!AnP} anp
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {void}
+ * @access private
+ */
+ const SettingsManager = function(anp, custom = null){
+
+ /** @type {SettingsManager} */
+ const self = this,
+ /** @type {Object.} */
+ settings = {},
+ /** @type {Object.} */
+ secrets = {};
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+ custom = Utils.get_dictionary(custom, true);
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ if(started)
+ return end(false);
+ started = true;
+
+ Utils.execute_items([
+ "default_settings_files", "default_settings", "settings_files", "settings"
+ ], (key, next) => {
+ self.add(self.get(key), next, true);
+ }, () => {
+ Utils.execute_items([
+ "default_secrets_files", "default_secrets", "secrets_files", "secrets"
+ ], (key, next) => {
+ self.add_secrets(self.get(key), next, true);
+ }, () => {
+ end(true);
+ });
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @param {?any} [_default = null]
+ * @returns {any|null}
+ * @access public
+ */
+ this.get = (keys, inputs = null, _default = null) => Utils.get_value(keys, [
+ inputs, custom, secrets, settings, SettingsManager.DEFAULT_SETTINGS
+ ], _default);
+
+ /**
+ * @param {!Object.} set
+ * @param {!(string|Object.|Array.)} inputs
+ * @param {?anp_settings_managers_default_callback} callback
+ * @param {!boolean} overwrite
+ * @returns {void}
+ * @access private
+ */
+ const add_to = (set, inputs, callback, overwrite) => {
+ anp.request.load_json(inputs, (data, subcallback) => {
+ for(const key in data)
+ !Check.is_key_mark(key) &&
+ (overwrite || set[key] === undefined) &&
+ (set[key] = data[key]);
+ Utils.execute(subcallback);
+ }, callback);
+ }
+
+ /**
+ * @param {!(string|Object.|Array.)} inputs
+ * @param {?anp_settings_managers_default_callback} [callback = null]
+ * @param {!boolean} [overwrite = false]
+ * @returns {void}
+ * @access public
+ */
+ this.add = (inputs, callback = null, overwrite = false) => {
+ add_to(settings, inputs, callback, overwrite);
+ };
+
+ /**
+ * @param {!(string|Object.|Array.)} inputs
+ * @param {?anp_settings_managers_default_callback} [callback = null]
+ * @param {!boolean} [overwrite = false]
+ * @returns {void}
+ * @access public
+ */
+ this.add_secrets = (inputs, callback = null, overwrite = false) => {
+ add_to(secrets, inputs, callback, overwrite);
+ };
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ SettingsManager.DEFAULT_SETTINGS = {
+ "autostart" : true,
+ "object_name" : "anp",
+ "default_settings_files" : [
+ "/json/AnP.settings.json",
+ "/json/AnP.ecma.settings.json"
+ ]
+ };
+
+ return SettingsManager;
+})()
\ No newline at end of file
diff --git a/Public/ecma/Managers/ThreadsManager.ecma.js b/Public/ecma/Managers/ThreadsManager.ecma.js
new file mode 100644
index 0000000..288b08f
--- /dev/null
+++ b/Public/ecma/Managers/ThreadsManager.ecma.js
@@ -0,0 +1,129 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+import {ThreadModel} from "../Models/ThreadModel.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_threads_manager_callback
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access public
+ */
+export const ThreadsManager = (function(){
+
+ /**
+ * @constructs ThreadsManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ */
+ const ThreadsManager = function(anp){
+
+ /** @type {ThreadsManager} */
+ const self = this,
+ /** @type {Array.} */
+ threads = [];
+ /** @type {boolean} */
+ let started = false,
+ /** @type {number|null} */
+ thread = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ thread = setInterval(try_execute_threads, 1000 / 60);
+
+ };
+
+ /**
+ * @param {?anp_start_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {!anp_start_callback} */
+ const end = ok => {
+ Utils.execute(callback, ok);
+ return ok;
+ };
+
+ return end(started ? false : started = true);
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const try_execute_threads = () => {
+ threads.forEach(thread => {
+ thread && thread.try_execute();
+ });
+ };
+
+ /**
+ * @param {!anp_threads_manager_callback} callback
+ * @param {?(Object.|Array.)} inputs
+ * @returns {ThreadModel}
+ * @access public
+ */
+ this.add = (callback, inputs = null) => {
+
+ /** @type {ThreadModel} */
+ const thread = new ThreadModel(anp, callback, inputs);
+
+ while(++ thread.i < threads.length)
+ if(!threads[thread.i])
+ break;
+ threads[thread.i] = thread;
+
+ return thread;
+ };
+
+ /**
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ * @access public
+ */
+ this.remove = thread => {
+ thread instanceof ThreadModel && threads[thread.i] && (threads[thread.i] = null);
+ };
+
+ /**
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ * @access public
+ */
+ this.play = thread => {
+ thread.working = true;
+ };
+
+ /**
+ * @param {!ThreadModel} thread
+ * @returns {void}
+ * @access public
+ */
+ this.stop = thread => {
+ thread.working = false;
+ };
+
+ constructor();
+
+ };
+
+ return ThreadsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Models/ThreadModel.ecma.js b/Public/ecma/Models/ThreadModel.ecma.js
new file mode 100644
index 0000000..c59e10f
--- /dev/null
+++ b/Public/ecma/Models/ThreadModel.ecma.js
@@ -0,0 +1,86 @@
+"use strict";
+
+import {Utils} from "../Utils/Utils.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @callback anp_thread_model_callback
+ * @param {ThreadModel} thread
+ * @returns {void}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @param {!AnP} anp
+ * @param {!(Object.|Array.)} inputs
+ * @access public
+ */
+export const ThreadModel = (function(){
+
+ /**
+ * @constructs ThreadModel
+ * @param {!AnP} anp
+ * @param {!anp_thread_model_callback} callback
+ * @param {!(Object.|Array.)} [inputs = null]
+ * @access private
+ */
+ const ThreadModel = function(anp, callback, inputs = null){
+
+ /** @type {ThreadModel} */
+ const self = this;
+
+ /** @type {anp_thread_model_callback} */
+ this.callback = callback;
+ /** @type {number} */
+ this.timer = anp.settings.get(["threads_timer", "timer"], inputs, 0);
+ /** @type {boolean} */
+ this.bucle = anp.settings.get(["threads_bucle", "bucle"], inputs, true);
+ /** @type {number} */
+ this.last_date = anp.settings.get(["threads_start_now", "start_now"], inputs, true) ? 0 : Date.now();
+ /** @type {number} */
+ this.i = -1;
+ /** @type {boolean} */
+ this.working = anp.settings.get(["threads_working", "working"], inputs, true);
+
+ /**
+ * @returns {void}
+ * @access public
+ */
+ this.try_execute = () => {
+ if(!self.working || self.i < 0)
+ return;
+
+ /** @type {number} */
+ const date = Date.now();
+
+ if(date - self.last_date > self.timer){
+ self.last_date = date;
+ Utils.execute(self.callback, self);
+ };
+
+ };
+
+ /**
+ * @returns {void}
+ * @access public
+ */
+ this.play = () => {
+ working = true;
+ };
+
+ /**
+ * @returns {void}
+ * @access public
+ */
+ this.stop = () => {
+ working = false;
+ };
+
+ };
+
+ return ThreadModel;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/Check.ecma.js b/Public/ecma/Utils/Check.ecma.js
new file mode 100644
index 0000000..e1519c2
--- /dev/null
+++ b/Public/ecma/Utils/Check.ecma.js
@@ -0,0 +1,137 @@
+"use strict";
+
+import {Patterns} from "./Patterns.ecma.js";
+
+/**
+ * @class
+ * @constructor
+ * @returns {void}
+ * @access public
+ */
+export const Check = (function(){
+
+ /**
+ * @constructs Check
+ * @returns {void}
+ * @access private
+ */
+ const Check = function(){};
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_array = item => item instanceof Array;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_dictionary = item => item && item.constructor == Object;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_string = item => typeof item == "string";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_key = item => Check.is_string(item) && Patterns.RE_KEY.test(item);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_pascal_key = item => Check.is_string(item) && Patterns.RE_PASCAL_KEY.test(item);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_function = item => typeof item == "function";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_null_or_undefined = item => item === null || item === undefined;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_html_item = item => item && (item.tagName || item.nodeName);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_html_items = item => item instanceof NodeList;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_bool = item => typeof item == "boolean";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_json_object = item => Check.is_array(item) || Check.is_dictionary(item);
+
+ /**
+ * @param {?any} items
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_array_string = items => Check.is_array(items) && items.every(Check.is_string);
+
+ /**
+ * @param {!string} key
+ * @param {!string} [mark = "AnP"]
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_key_mark = (key, mark = "AnP") => key.slice(0, mark.length + 1) == mark + "_" && (
+ key.slice(-4) == "_end" ||
+ key.slice(-6) == "_start"
+ );
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_number = item => typeof item == "number";
+
+ return Check;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/Patterns.ecma.js b/Public/ecma/Utils/Patterns.ecma.js
new file mode 100644
index 0000000..dc83042
--- /dev/null
+++ b/Public/ecma/Utils/Patterns.ecma.js
@@ -0,0 +1,27 @@
+"use strict";
+
+/**
+ * @class
+ * @constructor
+ * @returns {void}
+ * @access public
+ */
+export const Patterns = (function(){
+
+ /**
+ * @constructs Patterns
+ * @returns {void}
+ * @access private
+ */
+ const Patterns = function(){};
+
+ Patterns.RE_KEY = /^[a-z0-9_]+$/i;
+ Patterns.RE_PASCAL_KEY = /^[a-z0-9_\-]+$/i;
+ Patterns.RE_STRING_VARIABLES = /\{([a-z0-9_]+)\}/gi;
+ Patterns.RE_RIGHT_RANDOM_CHAIN = /^[a-z][a-z0-9]*$/i;
+ Patterns.RE_ATTRIBUTE_BAD_SET_CHARACTERS = /[^a-z0-9]+/gi;
+ Patterns.RE_SPACES = / +/gi;
+ Patterns.RE_CLEAN_EVENT_NAME = /^on[_\-]*/i;
+
+ return Patterns;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/Utils.ecma.js b/Public/ecma/Utils/Utils.ecma.js
new file mode 100644
index 0000000..bedc6b3
--- /dev/null
+++ b/Public/ecma/Utils/Utils.ecma.js
@@ -0,0 +1,241 @@
+"use strict";
+
+import {Check} from "./Check.ecma.js";
+import {Patterns} from "./Patterns.ecma.js";
+
+/**
+ * @callback anp_utils_default_callback
+ * @returns {void}
+ */
+
+/**
+ * @callback anp_utils_execute_callback
+ * @param {...any} [parameters]
+ * @returns {any|null}
+ */
+
+/**
+ * @callback anp_utils_execute_items_callback
+ * @param {?any} item
+ * @param {!anp_utils_default_callback} callback
+ * @returns {any|null}
+ */
+
+/**
+ * @class
+ * @constructor
+ * @returns {void}
+ * @access public
+ */
+export const Utils = (function(){
+
+ /**
+ * @constructs Utils
+ * @returns {void}
+ * @access private
+ */
+ const Utils = function(){};
+
+ /**
+ * @param {?any} item
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Utils.get_array = item => Check.is_array(item) ? item : [item];
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Utils.get_keys = keys => Utils.get_array(keys).filter((key, i, array) => (
+ Check.is_key(key) && array.indexOf(key) == i
+ ));
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Utils.get_pascal_keys = keys => Utils.get_array(keys).filter((key, i, array) => (
+ Check.is_pascal_key(key) && array.indexOf(key) == i
+ ));
+
+ /**
+ * @param {!(string|Array.)} strings
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Utils.get_strings = strings => Utils.get_array(strings).filter(string => Check.is_string(string));
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {!(Object.|Array.)} inputs
+ * @param {?any} [_default = null]
+ * @returns {any|null}
+ * @access public
+ * @static
+ */
+ Utils.get_value = (keys, inputs, _default = null) => {
+ if((keys = Utils.get_keys(keys)).length){
+ for(let i = 0, l = (inputs = Utils.get_array(inputs)).length; i < l; i ++){
+ if(Check.is_dictionary(inputs[i])){
+ for(let j = 0, m = keys.length; j < m; j ++)
+ if(inputs[i][keys[j]] !== undefined)
+ return inputs[i][keys[j]];
+ }else if(Check.is_array(inputs[i])){
+
+ /** @type {any|null|undefined} */
+ const response = Utils.get_value(keys, inputs[i], undefined);
+
+ if(response !== undefined)
+ return response;
+ };
+ };
+ };
+ return _default;
+ };
+
+ /**
+ * @param {!(Object.|Array.)} item
+ * @param {!boolean} [overwrite = false]
+ * @returns {Object.}
+ * @access public
+ * @static
+ */
+ Utils.get_dictionary = (item, overwrite = false) => Utils.get_array(item).reduce((dictionary, subitem) => {
+
+ /** @type {Object.} */
+ const results = (
+ Check.is_dictionary(subitem) ? subitem :
+ Check.is_array(subitem) ? Utils.get_dictionary(subitem, overwrite) :
+ {});
+
+ for(const key in results)
+ if(overwrite || dictionary[key] === undefined)
+ dictionary[key] = results[key];
+
+ return dictionary;
+ }, {});
+
+ /**
+ * @param {!anp_utils_execute_callback} callback
+ * @param {...any} [parameters]
+ * @returns {any|null}
+ * @access public
+ * @static
+ */
+ Utils.execute = (callback, ...parameters) => {
+ return Check.is_function(callback) ? callback(...parameters) : null;
+ };
+
+ /**
+ * @param {!Array.} items
+ * @param {!anp_utils_execute_items_callback} each_callback
+ * @param {?anp_utils_default_callback} [end_callback = null]
+ * @param {!number} [i = 0]
+ * @returns {void}
+ * @access public
+ * @static
+ */
+ Utils.execute_items = (items, each_callback, end_callback = null, i = 0) => {
+ // console.log([i, items.length, items]);
+ Utils.execute(...(
+ i == items.length ? [end_callback] :
+ [each_callback, items[i], () => {
+ Utils.execute_items(items, each_callback, end_callback, i + 1);
+ }]));
+ };
+
+ /**
+ * @param {!string} string
+ * @param {!(Object.|Array.)} variables
+ * @param {?any} [_default = null]
+ * @returns {string}
+ * @access public
+ * @static
+ */
+ Utils.string_variables = (string, variables, _default = null) => {
+
+ variables = Utils.get_dictionary(variables);
+
+ return ("" + string).replace(Patterns.RE_STRING_VARIABLES, (all, key) => (
+ variables[key] !== undefined ? variables[key] :
+ Check.is_null_or_undefined(_default) ? all :
+ _default));
+ };
+
+ /**
+ * @param {!(Object.|Array.)} data
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Utils.json_encode = data => {
+ try{
+ return JSON.stringify(data);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {!string} data
+ * @returns {Object.|Array.|null}
+ * @access public
+ * @static
+ */
+ Utils.json_decode = data => {
+ try{
+ return JSON.parse(data);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {!string} string
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Utils.base64_encode = string => {
+ try{
+ return btoa(string);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {!string} string
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Utils.base64_decode = string => {
+ try{
+ return atob(string);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {!(Object.|Array.)} variables
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Utils.variables_encode = variables => Utils.base64_encode(Utils.json_encode(variables));
+
+ /**
+ * @param {!string} variables
+ * @returns {Object.|Array.|null}
+ * @access public
+ * @static
+ */
+ Utils.variables_decode = variables => Utils.json_decode(Utils.base64_decode(variables));
+
+ return Utils
+})();
\ No newline at end of file
diff --git a/Public/favicon.ico b/Public/favicon.ico
new file mode 100644
index 0000000..cad6265
Binary files /dev/null and b/Public/favicon.ico differ
diff --git a/Public/images/AnP-180.png b/Public/images/AnP-180.png
new file mode 100755
index 0000000..5f0b5f4
Binary files /dev/null and b/Public/images/AnP-180.png differ
diff --git a/Public/images/AnP-192.png b/Public/images/AnP-192.png
new file mode 100755
index 0000000..4a3e8b7
Binary files /dev/null and b/Public/images/AnP-192.png differ
diff --git a/Public/images/AnP-270.png b/Public/images/AnP-270.png
new file mode 100755
index 0000000..9259dec
Binary files /dev/null and b/Public/images/AnP-270.png differ
diff --git a/Public/images/AnP-32.png b/Public/images/AnP-32.png
new file mode 100755
index 0000000..3d44a5e
Binary files /dev/null and b/Public/images/AnP-32.png differ
diff --git a/Public/images/AnP-512.png b/Public/images/AnP-512.png
new file mode 100755
index 0000000..9b0990f
Binary files /dev/null and b/Public/images/AnP-512.png differ
diff --git a/Public/images/AnP.png b/Public/images/AnP.png
new file mode 100755
index 0000000..9b0990f
Binary files /dev/null and b/Public/images/AnP.png differ
diff --git a/Public/index.html b/Public/index.html
new file mode 100644
index 0000000..9d33a08
--- /dev/null
+++ b/Public/index.html
@@ -0,0 +1,19 @@
+
+
+
+ Test - AnP
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Public/index.w.md b/Public/index.w.md
new file mode 100644
index 0000000..e1def9a
--- /dev/null
+++ b/Public/index.w.md
@@ -0,0 +1,12 @@
+```wmd-options
+language = es
+title_i18n = anp_title
+title_text = AnP
+since = 20250417
+```
+
+
+
+[[include /doc/es/index.w.md]]
+
+
\ No newline at end of file
diff --git a/Public/json/AnP.ecma.settings.json b/Public/json/AnP.ecma.settings.json
new file mode 100644
index 0000000..f15a5d6
--- /dev/null
+++ b/Public/json/AnP.ecma.settings.json
@@ -0,0 +1,57 @@
+{
+
+ "AnP_start" : null,
+ "autostart" : true,
+ "object_name" : "anp",
+ "application_name" : "AnP",
+ "application_git" : "https://git.k3y.pw/Lite/AnP",
+ "application_link" : "https://anp.k3y.pw/",
+ "application_version" : 20250419,
+ "application_licenses" : [
+ ["copyright", [2019, 2026], "KyMAN"],
+ ["cc_by_nc_sa_4"]
+ ],
+ "AnP_end" : null,
+
+ "AnP_SettingsManager_start" : null,
+ "default_settings_files" : [
+ "/json/AnP.settings.json",
+ "/json/AnP.ecma.settings.json"
+ ],
+ "default_secrets_files" : [
+ "/json/AnP.secrets.json",
+ "/json/AnP.ecma.secrets.json"
+ ],
+ "AnP_SettingsManager_end" : null,
+
+ "AnP_I18NManager_start" : null,
+ "default_language" : "english",
+ "language_selected" : "english",
+ "default_i18n_files" : [
+ "/json/i18n/common/AnP.i18n.english.json",
+ "/json/i18n/common/AnP.i18n.espanol.json",
+ "/json/i18n/common/AnP.i18n.galego.json",
+ "/json/i18n/common/AnP.i18n.nihongo.json",
+ "/json/i18n/common/AnP.i18n.russkiy.json",
+ "/json/i18n/ecma/AnP.ecma.i18n.english.json",
+ "/json/i18n/ecma/AnP.ecma.i18n.espanol.json",
+ "/json/i18n/ecma/AnP.ecma.i18n.galego.json",
+ "/json/i18n/ecma/AnP.ecma.i18n.nihongo.json",
+ "/json/i18n/ecma/AnP.ecma.i18n.russkiy.json"
+ ],
+ "AnP_I18NManager_end" : null,
+
+ "AnP_BaseComponent_start" : null,
+ "base_cells" : 40,
+ "base_minimum_size" : 12,
+ "base_zoom" : 100,
+ "base_gui_mode" : "default",
+ "base_footer_maximum_zoom" : 100,
+ "zoom_minimum" : 1,
+ "zoom_maximum" : 200,
+ "default_zoom" : 100,
+ "zoom_step" : 1,
+ "zoom_modes" : [50, 75, 100, 125, 150],
+ "AnP_BaseComponent_end" : null
+
+}
\ No newline at end of file
diff --git a/Public/json/AnP.go.settings.json b/Public/json/AnP.go.settings.json
new file mode 100644
index 0000000..1dd6cc9
--- /dev/null
+++ b/Public/json/AnP.go.settings.json
@@ -0,0 +1,4 @@
+{
+ "default_settings_files" : "/Public/json/AnP.go.settings.json",
+ "test" : "xD"
+}
\ No newline at end of file
diff --git a/Public/json/AnP.settings.json b/Public/json/AnP.settings.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/Public/json/AnP.settings.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/Public/json/i18n/common/AnP.i18n.english.json b/Public/json/i18n/common/AnP.i18n.english.json
new file mode 100644
index 0000000..c713dda
--- /dev/null
+++ b/Public/json/i18n/common/AnP.i18n.english.json
@@ -0,0 +1,3 @@
+{
+ "english" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/common/AnP.i18n.espanol.json b/Public/json/i18n/common/AnP.i18n.espanol.json
new file mode 100644
index 0000000..c2d5391
--- /dev/null
+++ b/Public/json/i18n/common/AnP.i18n.espanol.json
@@ -0,0 +1,22 @@
+{
+ "espanol" : {
+
+ "AnP_LicensesComponent_start" : null,
+ "cc_by_nc_sa_4" : "Creative Commons Atribución/Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional (CC-BY-NC-SA 4.0)",
+ "all_rights_reserved" : "Todos los derechos reservados.",
+ "copyright" : "© Copyright{year}{authors}{all_rights_reserved}",
+ "AnP_LicensesComponent_end" : null,
+
+ "AnP_BaseComponent_start" : null,
+ "gui_controls" : "Control del GUI",
+ "less_zoom" : "Menos Zoom",
+ "zoom" : "Zoom",
+ "reset_zoom" : "Reestablecer el Zoom",
+ "zoom_mode" : "Modo de Zoom",
+ "more_zoom" : "Menos Zoom",
+ "gui_mode" : "Modo del GUI",
+ "more_options" : "Más opciones",
+ "AnP_BaseComponent_end" : null
+
+ }
+}
\ No newline at end of file
diff --git a/Public/json/i18n/common/AnP.i18n.galego.json b/Public/json/i18n/common/AnP.i18n.galego.json
new file mode 100644
index 0000000..179eac4
--- /dev/null
+++ b/Public/json/i18n/common/AnP.i18n.galego.json
@@ -0,0 +1,22 @@
+{
+ "galego" : {
+
+ "AnP_LicensesComponent_start" : null,
+ "cc_by_nc_sa_4" : "Creative Commons Atribución/Recoñecemento-NonComercial-CompartirIgual 4.0 Internacional (CC-BY-NC-SA 4.0)",
+ "all_rights_reserved" : "Tódoslos dereitos reservados.",
+ "copyright" : "© Copyright{year}{authors}{all_rights_reserved}",
+ "AnP_LicensesComponent_end" : null,
+
+ "AnP_BaseComponent_start" : null,
+ "gui_controls" : "Control do GUI",
+ "less_zoom" : "Menos Zoom",
+ "zoom" : "Zoom",
+ "reset_zoom" : "Reestablece-lo Zoom",
+ "zoom_mode" : "Modo de Zoom",
+ "more_zoom" : "Menos Zoom",
+ "gui_mode" : "Modo do GUI",
+ "more_options" : "Máis opcións",
+ "AnP_BaseComponent_end" : null
+
+ }
+}
\ No newline at end of file
diff --git a/Public/json/i18n/common/AnP.i18n.nihongo.json b/Public/json/i18n/common/AnP.i18n.nihongo.json
new file mode 100644
index 0000000..eff18c6
--- /dev/null
+++ b/Public/json/i18n/common/AnP.i18n.nihongo.json
@@ -0,0 +1,3 @@
+{
+ "nihongo" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/common/AnP.i18n.russkiy.json b/Public/json/i18n/common/AnP.i18n.russkiy.json
new file mode 100644
index 0000000..b765480
--- /dev/null
+++ b/Public/json/i18n/common/AnP.i18n.russkiy.json
@@ -0,0 +1,3 @@
+{
+ "russkiy" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/ecma/AnP.ecma.i18n.english.json b/Public/json/i18n/ecma/AnP.ecma.i18n.english.json
new file mode 100644
index 0000000..c713dda
--- /dev/null
+++ b/Public/json/i18n/ecma/AnP.ecma.i18n.english.json
@@ -0,0 +1,3 @@
+{
+ "english" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/ecma/AnP.ecma.i18n.espanol.json b/Public/json/i18n/ecma/AnP.ecma.i18n.espanol.json
new file mode 100644
index 0000000..f464355
--- /dev/null
+++ b/Public/json/i18n/ecma/AnP.ecma.i18n.espanol.json
@@ -0,0 +1,3 @@
+{
+ "espanol" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/ecma/AnP.ecma.i18n.galego.json b/Public/json/i18n/ecma/AnP.ecma.i18n.galego.json
new file mode 100644
index 0000000..39f43be
--- /dev/null
+++ b/Public/json/i18n/ecma/AnP.ecma.i18n.galego.json
@@ -0,0 +1,3 @@
+{
+ "galego" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/ecma/AnP.ecma.i18n.nihongo.json b/Public/json/i18n/ecma/AnP.ecma.i18n.nihongo.json
new file mode 100644
index 0000000..eff18c6
--- /dev/null
+++ b/Public/json/i18n/ecma/AnP.ecma.i18n.nihongo.json
@@ -0,0 +1,3 @@
+{
+ "nihongo" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/ecma/AnP.ecma.i18n.russkiy.json b/Public/json/i18n/ecma/AnP.ecma.i18n.russkiy.json
new file mode 100644
index 0000000..b765480
--- /dev/null
+++ b/Public/json/i18n/ecma/AnP.ecma.i18n.russkiy.json
@@ -0,0 +1,3 @@
+{
+ "russkiy" : {}
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.base.scss b/Public/scss/AnP.base.scss
new file mode 100644
index 0000000..6c18f81
--- /dev/null
+++ b/Public/scss/AnP.base.scss
@@ -0,0 +1,3 @@
+.anp{
+ @include main_web()
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.common.scss b/Public/scss/AnP.common.scss
new file mode 100644
index 0000000..e0f9b10
--- /dev/null
+++ b/Public/scss/AnP.common.scss
@@ -0,0 +1,211 @@
+@use "sass:map";
+@use "sass:list";
+@use "sass:meta";
+
+@function unicode($code){
+ @return unquote("\"") + unquote(str-insert($code, "\\", 1)) + unquote("\"");
+}
+
+@function map-deep-get($scope, $keys...){
+
+ $i : 1;
+
+ @while (type-of($scope) == map) and ($i <= length($keys)){
+ $scope : map-get($scope, nth($keys, $i));
+ $i : $i + 1;
+ }
+
+ @return $scope;
+}
+
+@mixin main_color_web($mode){
+ background-color : map-deep-get($color, $mode, "back");
+ color : map-deep-get($color, $mode, fore);
+ &,button,input,select,textarea{color : map-deep-get($color, $mode, fore);}
+ button,input,select,textarea{background-color : map-deep-get($color, $mode, input-back);}
+ a[href]{
+ &[disabled]{color : map-deep-get($color, common, grey);}
+ &[readonly]{color : map-deep-get($color, $mode, fore);}
+ &:not([disabled],[readonly]){
+ color : map-deep-get($color, $mode, primary);
+ &:hover{color : map-deep-get($color, $mode, secondary);}
+ }
+ }
+ button,[type=button],[type=submit],[type=reset]{
+ &[disabled]{
+ border-color : map-deep-get($color, common, grey);
+ color : map-deep-get($color, common, grey);
+ box-shadow : 0em 0em .4em inset map-deep-get($color, common, grey);
+ }
+ &[readonly]{
+ border-color : map-deep-get($color, $mode, fore);
+ color : map-deep-get($color, $mode, fore);
+ box-shadow : 0em 0em .4em inset map-deep-get($color, common, grey);
+ }
+ &:not([disabled],[readonly]){
+ border-color : map-deep-get($color, $mode, primary);
+ color : mix(map-deep-get($color, $mode, primary), map-deep-get($color, $mode, fore), 50%);
+ box-shadow : 0em 0em .4em inset map-deep-get($color, $mode, primary);
+ &:hover{
+ border-color : map-deep-get($color, $mode, secondary);
+ color : mix(map-deep-get($color, $mode, secondary), map-deep-get($color, $mode, fore), 50%);
+ box-shadow : 0em 0em .4em inset map-deep-get($color, $mode, secondary);
+ }
+ }
+ }
+}
+
+@mixin main_web($reverse:false){
+
+ position : relative;
+ top : 0em;
+ left : 0em;
+ width : 100%;
+ height : 100%;
+ overflow : hidden;
+
+ &,button,input,select{font-family : $font-normal;}
+ textarea,pre{font-family : $font-mono;}
+ button,input,select,textarea{font-size : 1em;}
+ [data-icon]::before{
+ margin-right : .4em;
+ font-family : $font-icon;
+ }
+
+ a[href]{text-decoration : none;}
+ a[href],button,[type=button],[type=submit],[type=reset]{&:not([disabled],[readonly]){
+ cursor : pointer;
+ transition-duration : $transition-out;
+ transition-property : color;
+ &:hover{transition-duration : $transition-in;}
+ }}
+ button,[type=button],[type=submit],[type=reset],[type=text],[type=number],[type=date],[type=password],textarea{
+ padding : .1em .4em;
+ border-width : .1em;
+ border-style : solid;
+ border-color : map-deep-get($color, common, grey);
+ border-radius : $border-radius;
+ box-shadow : 0em 0em .4em inset map-deep-get($color, common, grey);
+ }
+ button,[type=button],[type=submit],[type=reset]{&:not([disabled],[readonly]){
+ transition-property : color,border-color,background-color,box-shadow;
+ }}
+
+ .group{
+ &>*{&,&>*{border-radius : 0em;}}
+ &>:first-child{&,&>:first-child{border-radius : $border-radius 0em 0em $border-radius;}}
+ &>:last-child{&,&>:last-child{border-radius : 0em $border-radius $border-radius 0em;}}
+ }
+
+ header,main,footer{
+ position : absolute;
+ left : 0em;
+ width : 100%;
+ }
+
+ header{
+ top : 0em;
+ height : $header-height;
+ z-index : 20;
+ overflow : visible;
+ }
+
+ main{
+ top : $header-height;
+ bottom : $footer-height;
+ z-index : 10;
+ overflow : auto;
+ }
+
+ footer{
+ display : flex;
+ flex-direction : row;
+ justify-items : center;
+ bottom : 0em;
+ height : $footer-height;
+ z-index : 30;
+ overflow : visible;
+ }
+
+ @each $key, $option in (dark : true, light : false){
+ &[data-gui-mode=#{$key}],&[data-gui-mode=default][data-dark-mode=#{$option}]{
+ @include main_color_web($key);
+ }
+ }
+
+ h1{
+ margin : .2em .5em;
+ padding : 0em;
+ font-size : 1em;
+ font-weight : 900;
+ img{
+ width : auto;
+ height : 3.6em;
+ &+span{display : none;}
+ }
+ a>span{vertical-align : middle;}
+ &,.image{display : inline-block;}
+ .text{
+ margin-left : .2em;
+ font-size : 3.6em;
+ }
+ }
+
+ .main-menu{
+ padding-bottom : .4em;
+ vertical-align : bottom;
+ &,ul,li{display : inline-block;}
+ ul{
+ margin : 0em;
+ padding : 0em;
+ list-style-type : none;
+ }
+ li{margin : 0em 1em;}
+ }
+
+ .licenses{
+ display : flex;
+ flex-direction : row;
+ justify-items : center;
+ align-items : center;
+ width : 100%;
+ font-weight : 100;
+ text-align : center;
+ img{
+ width : auto;
+ height : 1.8em;
+ }
+ &>span{
+ margin : 0em .3em;
+ vertical-align : middle;
+ }
+ .text{font-size : .7em;}
+ }
+ .copyright{font-size : .7em;}
+
+ .cc-by-nc-sa-4,.gplv3{
+ display : flex;
+ flex-direction : row;
+ justify-items : center;
+ align-items : center;
+ &>span{margin : 0em .3em;}
+ }
+
+ .gui-controls{
+ margin : .2em .5em;
+ padding : 0em;
+ border : none;
+ white-space : nowrap;
+ font-size : .85em;
+ &>*{&,&>*{border-radius : 0em;}}
+ &{&>[data-i18n=less_zoom],.input-number,&>[data-i18n=more_zoom],&>[data-i18n=reset_zoom]{display : none;}}
+ button[data-i18n=zoom_mode]{&,&>:first-child{border-radius : $border-radius 0em 0em $border-radius;}}
+ &>:last-child{&,&>:last-child{border-radius : 0em $border-radius $border-radius 0em;}}
+ legend{display : none;}
+ [data-icon]{
+ &::before{margin : 0em;}
+ &+[data-i18n]{display : none;}
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.css b/Public/scss/AnP.css
new file mode 100644
index 0000000..078615e
--- /dev/null
+++ b/Public/scss/AnP.css
@@ -0,0 +1,228 @@
+@use "sass:map";
+@use "sass:list";
+@use "sass:meta";
+.anp {
+ position: relative;
+ top: 0em;
+ left: 0em;
+ width: 100%;
+ height: 100%;
+ overflow: hidden; }
+ .anp, .anp button, .anp input, .anp select {
+ font-family: "Roboto", Arial; }
+ .anp textarea, .anp pre {
+ font-family: "RobotoMono", monospace; }
+ .anp button, .anp input, .anp select, .anp textarea {
+ font-size: 1em; }
+ .anp [data-icon]::before {
+ margin-right: .4em;
+ font-family: "FA6FS"; }
+ .anp a[href] {
+ text-decoration: none; }
+ .anp a[href]:not([disabled], [readonly]), .anp button:not([disabled], [readonly]), .anp [type=button]:not([disabled], [readonly]), .anp [type=submit]:not([disabled], [readonly]), .anp [type=reset]:not([disabled], [readonly]) {
+ cursor: pointer;
+ transition-duration: 1s;
+ transition-property: color; }
+ .anp a[href]:not([disabled], [readonly]):hover, .anp button:not([disabled], [readonly]):hover, .anp [type=button]:not([disabled], [readonly]):hover, .anp [type=submit]:not([disabled], [readonly]):hover, .anp [type=reset]:not([disabled], [readonly]):hover {
+ transition-duration: 0.35s; }
+ .anp button, .anp [type=button], .anp [type=submit], .anp [type=reset], .anp [type=text], .anp [type=number], .anp [type=date], .anp [type=password], .anp textarea {
+ padding: .1em .4em;
+ border-width: .1em;
+ border-style: solid;
+ border-color: #898989;
+ border-radius: 0.3em;
+ box-shadow: 0em 0em 0.4em inset #898989; }
+ .anp button:not([disabled], [readonly]), .anp [type=button]:not([disabled], [readonly]), .anp [type=submit]:not([disabled], [readonly]), .anp [type=reset]:not([disabled], [readonly]) {
+ transition-property: color,border-color,background-color,box-shadow; }
+ .anp .group > *, .anp .group > * > * {
+ border-radius: 0em; }
+ .anp .group > :first-child, .anp .group > :first-child > :first-child {
+ border-radius: 0.3em 0em 0em 0.3em; }
+ .anp .group > :last-child, .anp .group > :last-child > :last-child {
+ border-radius: 0em 0.3em 0.3em 0em; }
+ .anp header, .anp main, .anp footer {
+ position: absolute;
+ left: 0em;
+ width: 100%; }
+ .anp header {
+ top: 0em;
+ height: 4em;
+ z-index: 20;
+ overflow: visible; }
+ .anp main {
+ top: 4em;
+ bottom: 2em;
+ z-index: 10;
+ overflow: auto; }
+ .anp footer {
+ display: flex;
+ flex-direction: row;
+ justify-items: center;
+ bottom: 0em;
+ height: 2em;
+ z-index: 30;
+ overflow: visible; }
+ .anp[data-gui-mode=dark], .anp[data-gui-mode=default][data-dark-mode=true] {
+ background-color: #222;
+ color: #EFEFEF; }
+ .anp[data-gui-mode=dark], .anp[data-gui-mode=dark] button, .anp[data-gui-mode=dark] input, .anp[data-gui-mode=dark] select, .anp[data-gui-mode=dark] textarea, .anp[data-gui-mode=default][data-dark-mode=true], .anp[data-gui-mode=default][data-dark-mode=true] button, .anp[data-gui-mode=default][data-dark-mode=true] input, .anp[data-gui-mode=default][data-dark-mode=true] select, .anp[data-gui-mode=default][data-dark-mode=true] textarea {
+ color: #EFEFEF; }
+ .anp[data-gui-mode=dark] button, .anp[data-gui-mode=dark] input, .anp[data-gui-mode=dark] select, .anp[data-gui-mode=dark] textarea, .anp[data-gui-mode=default][data-dark-mode=true] button, .anp[data-gui-mode=default][data-dark-mode=true] input, .anp[data-gui-mode=default][data-dark-mode=true] select, .anp[data-gui-mode=default][data-dark-mode=true] textarea {
+ background-color: #1b1b1b; }
+ .anp[data-gui-mode=dark] a[href][disabled], .anp[data-gui-mode=default][data-dark-mode=true] a[href][disabled] {
+ color: #898989; }
+ .anp[data-gui-mode=dark] a[href][readonly], .anp[data-gui-mode=default][data-dark-mode=true] a[href][readonly] {
+ color: #EFEFEF; }
+ .anp[data-gui-mode=dark] a[href]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=true] a[href]:not([disabled], [readonly]) {
+ color: #2262b0; }
+ .anp[data-gui-mode=dark] a[href]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=true] a[href]:not([disabled], [readonly]):hover {
+ color: #b06222; }
+ .anp[data-gui-mode=dark] button[disabled], .anp[data-gui-mode=dark] [type=button][disabled], .anp[data-gui-mode=dark] [type=submit][disabled], .anp[data-gui-mode=dark] [type=reset][disabled], .anp[data-gui-mode=default][data-dark-mode=true] button[disabled], .anp[data-gui-mode=default][data-dark-mode=true] [type=button][disabled], .anp[data-gui-mode=default][data-dark-mode=true] [type=submit][disabled], .anp[data-gui-mode=default][data-dark-mode=true] [type=reset][disabled] {
+ border-color: #898989;
+ color: #898989;
+ box-shadow: 0em 0em 0.4em inset #898989; }
+ .anp[data-gui-mode=dark] button[readonly], .anp[data-gui-mode=dark] [type=button][readonly], .anp[data-gui-mode=dark] [type=submit][readonly], .anp[data-gui-mode=dark] [type=reset][readonly], .anp[data-gui-mode=default][data-dark-mode=true] button[readonly], .anp[data-gui-mode=default][data-dark-mode=true] [type=button][readonly], .anp[data-gui-mode=default][data-dark-mode=true] [type=submit][readonly], .anp[data-gui-mode=default][data-dark-mode=true] [type=reset][readonly] {
+ border-color: #EFEFEF;
+ color: #EFEFEF;
+ box-shadow: 0em 0em 0.4em inset #898989; }
+ .anp[data-gui-mode=dark] button:not([disabled], [readonly]), .anp[data-gui-mode=dark] [type=button]:not([disabled], [readonly]), .anp[data-gui-mode=dark] [type=submit]:not([disabled], [readonly]), .anp[data-gui-mode=dark] [type=reset]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=true] button:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=true] [type=button]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=true] [type=submit]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=true] [type=reset]:not([disabled], [readonly]) {
+ border-color: #2262b0;
+ color: #89a9d0;
+ box-shadow: 0em 0em 0.4em inset #2262b0; }
+ .anp[data-gui-mode=dark] button:not([disabled], [readonly]):hover, .anp[data-gui-mode=dark] [type=button]:not([disabled], [readonly]):hover, .anp[data-gui-mode=dark] [type=submit]:not([disabled], [readonly]):hover, .anp[data-gui-mode=dark] [type=reset]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=true] button:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=true] [type=button]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=true] [type=submit]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=true] [type=reset]:not([disabled], [readonly]):hover {
+ border-color: #b06222;
+ color: #d0a989;
+ box-shadow: 0em 0em 0.4em inset #b06222; }
+ .anp[data-gui-mode=light], .anp[data-gui-mode=default][data-dark-mode=false] {
+ background-color: #EFEFEF;
+ color: #222; }
+ .anp[data-gui-mode=light], .anp[data-gui-mode=light] button, .anp[data-gui-mode=light] input, .anp[data-gui-mode=light] select, .anp[data-gui-mode=light] textarea, .anp[data-gui-mode=default][data-dark-mode=false], .anp[data-gui-mode=default][data-dark-mode=false] button, .anp[data-gui-mode=default][data-dark-mode=false] input, .anp[data-gui-mode=default][data-dark-mode=false] select, .anp[data-gui-mode=default][data-dark-mode=false] textarea {
+ color: #222; }
+ .anp[data-gui-mode=light] button, .anp[data-gui-mode=light] input, .anp[data-gui-mode=light] select, .anp[data-gui-mode=light] textarea, .anp[data-gui-mode=default][data-dark-mode=false] button, .anp[data-gui-mode=default][data-dark-mode=false] input, .anp[data-gui-mode=default][data-dark-mode=false] select, .anp[data-gui-mode=default][data-dark-mode=false] textarea {
+ background-color: #f2f2f2; }
+ .anp[data-gui-mode=light] a[href][disabled], .anp[data-gui-mode=default][data-dark-mode=false] a[href][disabled] {
+ color: #898989; }
+ .anp[data-gui-mode=light] a[href][readonly], .anp[data-gui-mode=default][data-dark-mode=false] a[href][readonly] {
+ color: #222; }
+ .anp[data-gui-mode=light] a[href]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=false] a[href]:not([disabled], [readonly]) {
+ color: #2272D4; }
+ .anp[data-gui-mode=light] a[href]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=false] a[href]:not([disabled], [readonly]):hover {
+ color: #D47222; }
+ .anp[data-gui-mode=light] button[disabled], .anp[data-gui-mode=light] [type=button][disabled], .anp[data-gui-mode=light] [type=submit][disabled], .anp[data-gui-mode=light] [type=reset][disabled], .anp[data-gui-mode=default][data-dark-mode=false] button[disabled], .anp[data-gui-mode=default][data-dark-mode=false] [type=button][disabled], .anp[data-gui-mode=default][data-dark-mode=false] [type=submit][disabled], .anp[data-gui-mode=default][data-dark-mode=false] [type=reset][disabled] {
+ border-color: #898989;
+ color: #898989;
+ box-shadow: 0em 0em 0.4em inset #898989; }
+ .anp[data-gui-mode=light] button[readonly], .anp[data-gui-mode=light] [type=button][readonly], .anp[data-gui-mode=light] [type=submit][readonly], .anp[data-gui-mode=light] [type=reset][readonly], .anp[data-gui-mode=default][data-dark-mode=false] button[readonly], .anp[data-gui-mode=default][data-dark-mode=false] [type=button][readonly], .anp[data-gui-mode=default][data-dark-mode=false] [type=submit][readonly], .anp[data-gui-mode=default][data-dark-mode=false] [type=reset][readonly] {
+ border-color: #222;
+ color: #222;
+ box-shadow: 0em 0em 0.4em inset #898989; }
+ .anp[data-gui-mode=light] button:not([disabled], [readonly]), .anp[data-gui-mode=light] [type=button]:not([disabled], [readonly]), .anp[data-gui-mode=light] [type=submit]:not([disabled], [readonly]), .anp[data-gui-mode=light] [type=reset]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=false] button:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=false] [type=button]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=false] [type=submit]:not([disabled], [readonly]), .anp[data-gui-mode=default][data-dark-mode=false] [type=reset]:not([disabled], [readonly]) {
+ border-color: #2272D4;
+ color: #224a7b;
+ box-shadow: 0em 0em 0.4em inset #2272D4; }
+ .anp[data-gui-mode=light] button:not([disabled], [readonly]):hover, .anp[data-gui-mode=light] [type=button]:not([disabled], [readonly]):hover, .anp[data-gui-mode=light] [type=submit]:not([disabled], [readonly]):hover, .anp[data-gui-mode=light] [type=reset]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=false] button:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=false] [type=button]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=false] [type=submit]:not([disabled], [readonly]):hover, .anp[data-gui-mode=default][data-dark-mode=false] [type=reset]:not([disabled], [readonly]):hover {
+ border-color: #D47222;
+ color: #7b4a22;
+ box-shadow: 0em 0em 0.4em inset #D47222; }
+ .anp h1 {
+ margin: .2em .5em;
+ padding: 0em;
+ font-size: 1em;
+ font-weight: 900; }
+ .anp h1 img {
+ width: auto;
+ height: 3.6em; }
+ .anp h1 img + span {
+ display: none; }
+ .anp h1 a > span {
+ vertical-align: middle; }
+ .anp h1, .anp h1 .image {
+ display: inline-block; }
+ .anp h1 .text {
+ margin-left: .2em;
+ font-size: 3.6em; }
+ .anp .main-menu {
+ padding-bottom: .4em;
+ vertical-align: bottom; }
+ .anp .main-menu, .anp .main-menu ul, .anp .main-menu li {
+ display: inline-block; }
+ .anp .main-menu ul {
+ margin: 0em;
+ padding: 0em;
+ list-style-type: none; }
+ .anp .main-menu li {
+ margin: 0em 1em; }
+ .anp .licenses {
+ display: flex;
+ flex-direction: row;
+ justify-items: center;
+ align-items: center;
+ width: 100%;
+ font-weight: 100;
+ text-align: center; }
+ .anp .licenses img {
+ width: auto;
+ height: 1.8em; }
+ .anp .licenses > span {
+ margin: 0em .3em;
+ vertical-align: middle; }
+ .anp .licenses .text {
+ font-size: .7em; }
+ .anp .copyright {
+ font-size: .7em; }
+ .anp .cc-by-nc-sa-4, .anp .gplv3 {
+ display: flex;
+ flex-direction: row;
+ justify-items: center;
+ align-items: center; }
+ .anp .cc-by-nc-sa-4 > span, .anp .gplv3 > span {
+ margin: 0em .3em; }
+ .anp .gui-controls {
+ margin: .2em .5em;
+ padding: 0em;
+ border: none;
+ white-space: nowrap;
+ font-size: .85em; }
+ .anp .gui-controls > *, .anp .gui-controls > * > * {
+ border-radius: 0em; }
+ .anp .gui-controls > [data-i18n=less_zoom], .anp .gui-controls .input-number, .anp .gui-controls > [data-i18n=more_zoom], .anp .gui-controls > [data-i18n=reset_zoom] {
+ display: none; }
+ .anp .gui-controls button[data-i18n=zoom_mode], .anp .gui-controls button[data-i18n=zoom_mode] > :first-child {
+ border-radius: 0.3em 0em 0em 0.3em; }
+ .anp .gui-controls > :last-child, .anp .gui-controls > :last-child > :last-child {
+ border-radius: 0em 0.3em 0.3em 0em; }
+ .anp .gui-controls legend {
+ display: none; }
+ .anp .gui-controls [data-icon]::before {
+ margin: 0em; }
+ .anp .gui-controls [data-icon] + [data-i18n] {
+ display: none; }
+
+.anp [data-icon=less_zoom]::before {
+ content: "\f010"; }
+.anp [data-icon=more_zoom]::before {
+ content: "\f00e"; }
+.anp [data-icon=reset_zoom]::before {
+ content: "\f002"; }
+.anp [data-icon=zoom_mode]::before {
+ content: "\f689"; }
+.anp [data-icon=more_options]::before {
+ content: "\f013"; }
+.anp [data-icon=home]::before {
+ content: "\f015"; }
+.anp [data-icon=web]::before {
+ content: "\f0ac"; }
+.anp [data-icon=git]::before {
+ content: "\f841";
+ font-family: "FA6FB"; }
+.anp [data-icon=documentation]::before {
+ content: "\f15c";
+ font-family: "FA6FR"; }
+.anp[data-gui-mode=default] [data-icon=gui_mode]::before {
+ content: "\f850"; }
+.anp[data-gui-mode=dark] [data-icon=gui_mode]::before {
+ content: "\f3fb"; }
+.anp[data-gui-mode=light] [data-icon=gui_mode]::before {
+ content: "\f009"; }
+
+/*# sourceMappingURL=AnP.css.map */
diff --git a/Public/scss/AnP.css.map b/Public/scss/AnP.css.map
new file mode 100644
index 0000000..32b357a
--- /dev/null
+++ b/Public/scss/AnP.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": "AAAA,eAAe;AACf,gBAAgB;AAChB,gBAAgB;ACFhB,IAAI;ED2DA,QAAQ,EAAG,QAAQ;EACnB,GAAG,EAAG,GAAG;EACT,IAAI,EAAG,GAAG;EACV,KAAK,EAAG,IAAI;EACZ,MAAM,EAAG,IAAI;EACb,QAAQ,EAAG,MAAM;EAEjB,0CAAqB;IAAC,WAAW,EEnCtB,eAAe;EFoC1B,uBAAY;IAAC,WAAW,EEnCf,uBAAuB;EFoChC,mDAA4B;IAAC,SAAS,EAAG,GAAG;EAC5C,wBAAmB;IACf,YAAY,EAAG,IAAI;IACnB,WAAW,EEtCN,OAAO;EFyChB,YAAO;IAAC,eAAe,EAAG,IAAI;EAC0B,gOAA4B;IAChF,MAAM,EAAG,OAAO;IAChB,mBAAmB,EExCT,EAAE;IFyCZ,mBAAmB,EAAG,KAAK;IAC3B,8PAAO;MAAC,mBAAmB,EE3ClB,KAAI;EF6CjB,mKAA8G;IAC1G,OAAO,EAAG,SAAS;IACnB,YAAY,EAAG,IAAI;IACnB,YAAY,EAAG,KAAK;IACpB,YAAY,EAxEH,OAA+B;IAyExC,aAAa,EE1DJ,KAAI;IF2Db,UAAU,EAAG,2BAAqD;EAEtB,sLAA4B;IACxE,mBAAmB,EAAG,8CAA8C;EAIhE,oCAAK;IAAC,aAAa,EAAG,GAAG;EACd,qEAAgB;IAAC,aAAa,EAAG,mBAAqC;EACvE,kEAAe;IAAC,aAAa,EAAG,mBAAqC;EAGvF,mCAAkB;IACd,QAAQ,EAAG,QAAQ;IACnB,IAAI,EAAG,GAAG;IACV,KAAK,EAAG,IAAI;EAGhB,WAAM;IACF,GAAG,EAAG,GAAG;IACT,MAAM,EEjFG,GAAG;IFkFZ,OAAO,EAAG,EAAE;IACZ,QAAQ,EAAG,OAAO;EAGtB,SAAI;IACA,GAAG,EEvFM,GAAG;IFwFZ,MAAM,EEvFG,GAAG;IFwFZ,OAAO,EAAG,EAAE;IACZ,QAAQ,EAAG,IAAI;EAGnB,WAAM;IACF,OAAO,EAAG,IAAI;IACd,cAAc,EAAG,GAAG;IACpB,aAAa,EAAG,MAAM;IACtB,MAAM,EAAG,GAAG;IACZ,MAAM,EEjGG,GAAG;IFkGZ,OAAO,EAAG,EAAE;IACZ,QAAQ,EAAG,OAAO;EAIlB,0EAA4E;IA7GhF,gBAAgB,EARH,IAA+B;IAS5C,KAAK,EATQ,OAA+B;IAU5C,obAA8B;MAAC,KAAK,EAVvB,OAA+B;IAW5C,wWAA4B;MAAC,gBAAgB,EAXhC,OAA+B;IAaxC,8GAAW;MAAC,KAAK,EAbR,OAA+B;IAcxC,8GAAW;MAAC,KAAK,EAdR,OAA+B;IAexC,kJAA4B;MACxB,KAAK,EAhBA,OAA+B;MAiBpC,8JAAO;QAAC,KAAK,EAjBR,OAA+B;IAqBxC,8dAAW;MACP,YAAY,EAtBP,OAA+B;MAuBpC,KAAK,EAvBA,OAA+B;MAwBpC,UAAU,EAAG,2BAAqD;IAEtE,8dAAW;MACP,YAAY,EA3BP,OAA+B;MA4BpC,KAAK,EA5BA,OAA+B;MA6BpC,UAAU,EAAG,2BAAqD;IAEtE,8mBAA4B;MACxB,YAAY,EAhCP,OAA+B;MAiCpC,KAAK,EAAG,OAAiF;MACzF,UAAU,EAAG,2BAAuD;MACpE,8pBAAO;QACH,YAAY,EApCX,OAA+B;QAqChC,KAAK,EAAG,OAAmF;QAC3F,UAAU,EAAG,2BAAyD;EA+E9E,4EAA4E;IA7GhF,gBAAgB,EARH,OAA+B;IAS5C,KAAK,EATQ,IAA+B;IAU5C,8bAA8B;MAAC,KAAK,EAVvB,IAA+B;IAW5C,gXAA4B;MAAC,gBAAgB,EAXhC,OAA+B;IAaxC,gHAAW;MAAC,KAAK,EAbR,OAA+B;IAcxC,gHAAW;MAAC,KAAK,EAdR,IAA+B;IAexC,oJAA4B;MACxB,KAAK,EAhBA,OAA+B;MAiBpC,gKAAO;QAAC,KAAK,EAjBR,OAA+B;IAqBxC,seAAW;MACP,YAAY,EAtBP,OAA+B;MAuBpC,KAAK,EAvBA,OAA+B;MAwBpC,UAAU,EAAG,2BAAqD;IAEtE,seAAW;MACP,YAAY,EA3BP,IAA+B;MA4BpC,KAAK,EA5BA,IAA+B;MA6BpC,UAAU,EAAG,2BAAqD;IAEtE,snBAA4B;MACxB,YAAY,EAhCP,OAA+B;MAiCpC,KAAK,EAAG,OAAiF;MACzF,UAAU,EAAG,2BAAuD;MACpE,sqBAAO;QACH,YAAY,EApCX,OAA+B;QAqChC,KAAK,EAAG,OAAmF;QAC3F,UAAU,EAAG,2BAAyD;EAoFlF,OAAE;IACE,MAAM,EAAG,SAAS;IAClB,OAAO,EAAG,GAAG;IACb,SAAS,EAAG,GAAG;IACf,WAAW,EAAG,GAAG;IACjB,WAAG;MACC,KAAK,EAAG,IAAI;MACZ,MAAM,EAAG,KAAK;MACd,kBAAM;QAAC,OAAO,EAAG,IAAI;IAEzB,gBAAM;MAAC,cAAc,EAAG,MAAM;IAC9B,uBAAQ;MAAC,OAAO,EAAG,YAAY;IAC/B,aAAK;MACD,WAAW,EAAG,IAAI;MAClB,SAAS,EAAG,KAAK;EAIzB,eAAU;IACN,cAAc,EAAG,IAAI;IACrB,cAAc,EAAG,MAAM;IACvB,uDAAO;MAAC,OAAO,EAAG,YAAY;IAC9B,kBAAE;MACE,MAAM,EAAG,GAAG;MACZ,OAAO,EAAG,GAAG;MACb,eAAe,EAAG,IAAI;IAE1B,kBAAE;MAAC,MAAM,EAAG,OAAO;EAGvB,cAAS;IACL,OAAO,EAAG,IAAI;IACd,cAAc,EAAG,GAAG;IACpB,aAAa,EAAG,MAAM;IACtB,WAAW,EAAG,MAAM;IACpB,KAAK,EAAG,IAAI;IACZ,WAAW,EAAG,GAAG;IACjB,UAAU,EAAG,MAAM;IACnB,kBAAG;MACC,KAAK,EAAG,IAAI;MACZ,MAAM,EAAG,KAAK;IAElB,qBAAM;MACF,MAAM,EAAG,QAAQ;MACjB,cAAc,EAAG,MAAM;IAE3B,oBAAK;MAAC,SAAS,EAAG,IAAI;EAE1B,eAAU;IAAC,SAAS,EAAG,IAAI;EAE3B,gCAAqB;IACjB,OAAO,EAAG,IAAI;IACd,cAAc,EAAG,GAAG;IACpB,aAAa,EAAG,MAAM;IACtB,WAAW,EAAG,MAAM;IACpB,8CAAM;MAAC,MAAM,EAAG,QAAQ;EAG5B,kBAAa;IACT,MAAM,EAAG,SAAS;IAClB,OAAO,EAAG,GAAG;IACb,MAAM,EAAG,IAAI;IACb,WAAW,EAAG,MAAM;IACpB,SAAS,EAAG,KAAK;IACb,kDAAK;MAAC,aAAa,EAAG,GAAG;IAC3B,qKAAsF;MAAC,OAAO,EAAG,IAAI;IAC3E,6GAAgB;MAAC,aAAa,EAAG,mBAAqC;IACpF,gFAAe;MAAC,aAAa,EAAG,mBAAqC;IACnF,yBAAM;MAAC,OAAO,EAAG,IAAI;IAEjB,sCAAS;MAAC,MAAM,EAAG,GAAG;IACtB,4CAAa;MAAC,OAAO,EAAG,IAAI;;AGnMhC,kCAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,kCAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,mCAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,kCAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,qCAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,6BAA4B;EAAC,OAAO,EAAG,OAAc;AAArD,4BAA4B;EAAC,OAAO,EAAG,OAAc;AAMrD,4BAA4B;EAAC,OAAO,EAAG,OAAc;EAAE,WAAW,EAAG,OAAO;AAM5E,sCAA4B;EAAC,OAAO,EAAG,OAAc;EAAE,WAAW,EAAG,OAAO;AAQ5E,wDAAsD;EAAC,OAAO,EAAG,OAAc;AAA/E,qDAAsD;EAAC,OAAO,EAAG,OAAc;AAA/E,sDAAsD;EAAC,OAAO,EAAG,OAAc",
+"sources": ["AnP.common.scss","AnP.base.scss","AnP.settings.scss","AnP.icons.scss"],
+"names": [],
+"file": "AnP.css"
+}
diff --git a/Public/scss/AnP.icons.scss b/Public/scss/AnP.icons.scss
new file mode 100644
index 0000000..0241c67
--- /dev/null
+++ b/Public/scss/AnP.icons.scss
@@ -0,0 +1,35 @@
+.anp{
+
+ @each $name, $code in (
+ "less_zoom" : "f010",
+ "more_zoom" : "f00e",
+ "reset_zoom" : "f002",
+ "zoom_mode" : "f689",
+ "more_options" : "f013",
+ "home" : "f015",
+ "web" : "f0ac"
+ ){
+ [data-icon=#{$name}]::before{content : unicode($code);}
+ }
+
+ @each $name, $code in (
+ "git" : "f841"
+ ){
+ [data-icon=#{$name}]::before{content : unicode($code); font-family : "FA6FB";}
+ }
+
+ @each $name, $code in (
+ "documentation" : "f15c"
+ ){
+ [data-icon=#{$name}]::before{content : unicode($code); font-family : "FA6FR";}
+ }
+
+ @each $mode, $code in (
+ "default" : "f850",
+ "dark" : "f3fb",
+ "light" : "f009",
+ ){
+ &[data-gui-mode=#{$mode}] [data-icon=gui_mode]::before{content : unicode($code);}
+ }
+
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.scss b/Public/scss/AnP.scss
new file mode 100644
index 0000000..880eb0f
--- /dev/null
+++ b/Public/scss/AnP.scss
@@ -0,0 +1 @@
+@import "AnP.settings.scss", "AnP.common.scss", "AnP.base.scss", "AnP.icons.scss";
\ No newline at end of file
diff --git a/Public/scss/AnP.settings.scss b/Public/scss/AnP.settings.scss
new file mode 100644
index 0000000..6cac911
--- /dev/null
+++ b/Public/scss/AnP.settings.scss
@@ -0,0 +1,39 @@
+// Colors.
+$color-fore : #222;
+$color-back : #EFEFEF;
+$color-primary : #2272D4;
+$color-secondary : #D47222;
+$color : (
+ light : (
+ fore : $color-fore,
+ back : $color-back,
+ primary : $color-primary,
+ secondary : $color-secondary,
+ input-back : mix($color-back, #FFF, 80%)
+ ),
+ dark : (
+ fore : $color-back,
+ back : $color-fore,
+ primary : mix($color-primary, $color-fore, 80%),
+ secondary : mix($color-secondary, $color-fore, 80%),
+ input-back : mix($color-fore, #000, 80%)
+ ),
+ common : (
+ grey : mix($color-fore, $color-back, 50%)
+ )
+);
+
+// Sizes.
+$header-height : 4em;
+$footer-height : 2em;
+$border-radius : .3em;
+
+// Fonts.
+$font-normal : "Roboto", Arial;
+$font-mono : "RobotoMono", monospace;
+$font-icon : "FA6FS";
+
+// Transitions.
+$transition-in : .35s;
+$transition-out : 1s;
+$transition : .5s;
\ No newline at end of file
diff --git a/Public/test.anp.html b/Public/test.anp.html
new file mode 100644
index 0000000..37475e2
--- /dev/null
+++ b/Public/test.anp.html
@@ -0,0 +1,35 @@
+
+
+
+ Test - AnP
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Public/test.w.md b/Public/test.w.md
new file mode 100644
index 0000000..cfc48d8
--- /dev/null
+++ b/Public/test.w.md
@@ -0,0 +1,13 @@
+```wmd-options
+language = es
+title_i18n = anp_test
+title_text = Test - AnP
+```
+
+
+
+# AnP
+
+Esto es ***una*** prueba.
+
+
\ No newline at end of file
diff --git a/Public/tests.w.md b/Public/tests.w.md
new file mode 100644
index 0000000..1ca1645
--- /dev/null
+++ b/Public/tests.w.md
@@ -0,0 +1,237 @@
+```wmd-options
+language = es
+title_i18n = wmarkdown_title_tests
+title_text = Tests - WMarkDown
+```
+
+
+
+## Tests
+
+Tests de Checks:
+
+[| class="wmd-checks-viewer"
+|=| Marca |
+|= Upper | Lower | Valor
+| \[ ] | \[ ] | [ ]
+| \[X] | \[x] | [x]
+| \[-] | \[-] | [-]
+| \( ) | \( ) | ( )
+| \(X) | \(x) | (x)
+| \(-) | \(-) | (-)
+| \{V} | \{v} | {v}
+| \{W} | \{w} | {w}
+| \{X} | \{x} | {x}
+|]
+
+Con esto, esto [X] es un Checkbox.
+
+Esto es una prueba de ***Italic-Bold***. ¿Funca? ¿Y si *ponemos esto **con negrilla**, funcionaría*?
+
+Bloque de Flowcharts de Mermaid JS:
+
+```mermaid
+flowchart TD
+
+A --> B
+B --> C
+```
+
+Bloque de funciones matemáticas:
+
+```maths
+
+f(x) = a + bc + c
+
+```
+
+Este elemento [[#F00]], que es lo mismo que [[color #F00]] y [[color red]], es una prueba de muestra de color rojo.
+
+[[@ [Integer|String|Array] ErrorsManager.bitwise(!Integer | String | Array code = "jojo", !Integer bits)]]
+
+Imagen:
+
+((!image https://images.unsplash.com/photo-1597852074816-d933c7d2b988 Esto es una prueba xD))
+
+((!picture https://images.unsplash.com/photo-1597852074816-d933c7d2b988 Esto es una prueba xD))
+
+Y esto sería un icono ((!icon https://images.unsplash.com/photo-1597852074816-d933c7d2b988 Esto es una prueba xD)) xD
+
+* Elemento A
+* Elemento B
+* Elemento C
+
+- Elemento A
+- Elemento B
+ - JoJo
+- Elemento C
+
++ Elemento A
++ Elemento B
+ - jojo
++ Elemento C
+
+# Elemento A
+# Elemento B
+#* Elemento BA
+#* Elemento BB
+#*# Elemento BBA
+# Elemento C
+
+1. Elemento A
+2. Elemento B
+3. Elemento C
+
+a. Elemento A
+b. Elemento B
+c. Elemento C
+
+A. Elemento A
+B. Elemento B
+C. Elemento C
+
+i. Elemento A
+i. Elemento B
+i. Elemento C
+
+i. Elemento A
+ii. Elemento B
+iii. Elemento C
+
+I. Elemento A
+I. Elemento B
+I. Elemento C
+
+4. Elemento 1.
++ Elemento 2.
+ # Elemento 2-1.
+ # Elemento 2-2.
+ # Elemento 2-3.
+* Elemento 3.
+ # Elemento 3-1.
+ # Elemento 3-2.
+ # Elemento 3-3.
+- Elemento 4.
+ # Elemento 4-1.
+ # Elemento 4-2.
+ # Elemento 4-3.
+* Elemento N.
+ # Elemento N-1.
+ # Elemento N-2.
+ # Elemento N-3.
+
+> [!@Srx00] jojo xDDD
+
+Esto es un texto ~~-tachado-~~ xDDD.
+
+Y esto es un texto __subrayado__ xDD.
+
+##### Esto sería un H5.
+
+###### Esto sería un H6.
+
+https://wmarkdown.k3y.pw/
+
+kyman@wmarkdown.k3y.pw
+
+[+94 683 43 12 441]
+
+===== Esto sería un H5. =====
+
+====== Esto sería un H6. ======
+
+También puedes poner direcciones como Cryptomonedas como [bitcoin:tb1qujswuek3mefsm3m84g94ek40drysxe9z09yfnc tb1qujswuek3mefsm3m84g94ek40drysxe9z09yfnc] o [litecoin:tltc1q4xy8sg2hf6nqqrhp69aukn6md3rx8v6jlqc67s tltc1q4xy8sg2hf6nqqrhp69aukn6md3rx8v6jlqc67s] entre otras.
+
+También puedes poner direcciones como Cryptomonedas como [tb1qujswuek3mefsm3m84g94ek40drysxe9z09yfnc](bitcoin:tb1qujswuek3mefsm3m84g94ek40drysxe9z09yfnc Dirección Bitcoin TestNet) o [tltc1q4xy8sg2hf6nqqrhp69aukn6md3rx8v6jlqc67s](litecoin:tltc1q4xy8sg2hf6nqqrhp69aukn6md3rx8v6jlqc67s Dirección Litecoin TestNet) entre otras.
+
+A continuación montaremos una tabla.
+
+[| class="jojo"
+|^ Cabecera 1 | Cabecera 2 | Cabecera 3 | Cabecera N
+|= Título 1 | Título 2 | Título 3 | Título N
+| Elemento 1 | Elemento 2 | Elemento 3 | Elemento N
+| Elemento 1 | Elemento 2 | Elemento 3 | Elemento N
+| Elemento 1 | Elemento 2 | Elemento 3 | Elemento N
+|_ Pie 1 | Pie 2 | Pie 3 | Pie N
+|]
+
+[|
+|= Columna 1 | Columna 2 | Columna 3 | Columna 4 | Columna 5
+| Columna 1 | Columna 2 | Columna 3 | Columna 4 | Columna 5
+||| Columna 1-3 | Columna 4 | Columna 5
+| Columna 1 | *Columna* 2 ||| Columna 3-5
+| Columna 1 || Columna 2-3 | [https://wmarkdown.k3y.pw/#tablas Columna 4] | Columna 5
+|]
+
+[|
+|= Columna 1 | Columna 2 | Columna 3
+| Celda 1-A | Celda 2-A | Celda 3-A
+| Celda 1-B | "Celda 2-B
+
+Esto es más contenido de la celda 2-B." | Celda 3-B
+| Celda 1-C | 'Celda 2-C
+
+Esto es más contenido de la celda 2-C.' | Celda 3-C
+|]
+
+## Code Sample Block Test
+
+
+
+```js
+console.log("PASA");
+```
+
+```py
+print("PASA")
+```
+
+¿Funciona? xD
+
+```rust
+fn main(){
+ println!(String::from("PASA"));
+}
+```
+
+```go
+fn main(){
+ println("PASA")
+}
+```
+
+```c
+print("PASA");
+```
+
+```c++
+#include
+#include
+
+std::cout << "PASA" << std::endl;
+
+```
+
+```c#
+debug.println("a", "PASA");
+```
+
+```java
+system.out.println("PASA");
+```
+
+```php
+echo "PASA";
+```
+
+```as
+trace("PASA");
+```
+
+```sql
+select "PASA" as x
+```
+
+
+
+
\ No newline at end of file
diff --git a/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.base.scssc b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.base.scssc
new file mode 100644
index 0000000..d164fcb
Binary files /dev/null and b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.base.scssc differ
diff --git a/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.common.scssc b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.common.scssc
new file mode 100644
index 0000000..8091ecd
Binary files /dev/null and b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.common.scssc differ
diff --git a/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.icons.scssc b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.icons.scssc
new file mode 100644
index 0000000..924641e
Binary files /dev/null and b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.icons.scssc differ
diff --git a/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.scssc b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.scssc
new file mode 100644
index 0000000..0c5dc8f
Binary files /dev/null and b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.scssc differ
diff --git a/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.settings.scssc b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.settings.scssc
new file mode 100644
index 0000000..ee6761a
Binary files /dev/null and b/Tools/.sass-cache/0a8d081567b7db68ac740490199389ac3b342dd1/AnP.settings.scssc differ
diff --git a/Tools/run.go.server.sh b/Tools/run.go.server.sh
new file mode 100755
index 0000000..bcada8a
--- /dev/null
+++ b/Tools/run.go.server.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+cd ../Go
+go build -o ../Bin/AnP.golang.debug Main/Server/Main.go
+../Bin/AnP.golang.debug
+# rm ../Bin/AnP.golang.debug
+cd ../Tools
\ No newline at end of file
diff --git a/Tools/run.go.test.sh b/Tools/run.go.test.sh
new file mode 100755
index 0000000..14d9e2f
--- /dev/null
+++ b/Tools/run.go.test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+cd ../Go
+go build -o ../Bin/AnP.test.golang.debug Main/Test/Main.go
+../Bin/AnP.test.golang.debug
+# rm ../Bin/AnP.test.golang.debug
+cd ../Tools
\ No newline at end of file
diff --git a/Tools/sass.sh b/Tools/sass.sh
new file mode 100755
index 0000000..67b6c2c
--- /dev/null
+++ b/Tools/sass.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+directory=`dirname $(readlink -f "$0")`
+sass $directory/../Public/scss/AnP.scss ../Public/scss/AnP.css;
+# sass $directory/../Public/scss/AnPWeb.scss ../Public/scss/AnPWeb.css;
diff --git a/version b/version
new file mode 100644
index 0000000..8a9ecc2
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+0.0.1
\ No newline at end of file