forked from mbk-lab/rui_orig
				
			Added Theme interface, NewTheme, CreateThemeFromText, and AddTheme functions
This commit is contained in:
		
							parent
							
								
									0286918dd4
								
							
						
					
					
						commit
						894242f2dd
					
				| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
* Added "user-data" property
 | 
					* Added "user-data" property
 | 
				
			||||||
* Added "focusable" property
 | 
					* Added "focusable" property
 | 
				
			||||||
* Added ReloadTableViewData, AllImageResources functions
 | 
					* Added ReloadTableViewData, AllImageResources functions
 | 
				
			||||||
 | 
					* Added Theme interface, NewTheme, CreateThemeFromText, and AddTheme functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v0.5.0
 | 
					# v0.5.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -554,7 +554,9 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cssBuilder.endAnimation()
 | 
						cssBuilder.endAnimation()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	style := strings.ReplaceAll(cssBuilder.finish(), "\n", `\n`)
 | 
						style := cssBuilder.finish()
 | 
				
			||||||
 | 
						session.animationCSS += style
 | 
				
			||||||
 | 
						style = strings.ReplaceAll(style, "\n", `\n`)
 | 
				
			||||||
	session.runScript(`document.querySelector('style').textContent += "` + style + `"`)
 | 
						session.runScript(`document.querySelector('style').textContent += "` + style + `"`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return name
 | 
						return name
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,6 +137,18 @@ function getIntAttribute(element, tag) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function scanElementsSize() {
 | 
					function scanElementsSize() {
 | 
				
			||||||
 | 
						var element = document.getElementById("ruiRootView");
 | 
				
			||||||
 | 
						if (element) {
 | 
				
			||||||
 | 
							let rect = element.getBoundingClientRect();
 | 
				
			||||||
 | 
							let width = getIntAttribute(element, "data-width");
 | 
				
			||||||
 | 
							let height = getIntAttribute(element, "data-height");
 | 
				
			||||||
 | 
							if (rect.width > 0 && rect.height > 0 && (width != rect.width || height != rect.height)) {
 | 
				
			||||||
 | 
								element.setAttribute("data-width", rect.width);
 | 
				
			||||||
 | 
								element.setAttribute("data-height", rect.height);
 | 
				
			||||||
 | 
								sendMessage("root-size{session=" + sessionID + ",width=" + rect.width + ",height=" + rect.height +"}");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	var views = document.getElementsByClassName("ruiView");
 | 
						var views = document.getElementsByClassName("ruiView");
 | 
				
			||||||
	if (views) {
 | 
						if (views) {
 | 
				
			||||||
		var message = "resize{session=" + sessionID + ",views=["
 | 
							var message = "resize{session=" + sessionID + ",views=["
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,11 +90,6 @@ func (app *application) getStartPage() string {
 | 
				
			||||||
	return buffer.String()
 | 
						return buffer.String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (app *application) init(params AppParams) {
 | 
					 | 
				
			||||||
	app.params = params
 | 
					 | 
				
			||||||
	app.sessions = map[int]Session{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (app *application) Start(addr string) {
 | 
					func (app *application) Start(addr string) {
 | 
				
			||||||
	http.Handle("/", app)
 | 
						http.Handle("/", app)
 | 
				
			||||||
	log.Fatal(http.ListenAndServe(addr, nil))
 | 
						log.Fatal(http.ListenAndServe(addr, nil))
 | 
				
			||||||
| 
						 | 
					@ -104,7 +99,6 @@ func (app *application) Finish() {
 | 
				
			||||||
	for _, session := range app.sessions {
 | 
						for _, session := range app.sessions {
 | 
				
			||||||
		session.close()
 | 
							session.close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (app *application) nextSessionID() int {
 | 
					func (app *application) nextSessionID() int {
 | 
				
			||||||
| 
						 | 
					@ -250,6 +244,9 @@ func sessionEventHandler(session Session, events chan DataObject, brige WebBrige
 | 
				
			||||||
		case "session-resume":
 | 
							case "session-resume":
 | 
				
			||||||
			session.onResume()
 | 
								session.onResume()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case "root-size":
 | 
				
			||||||
 | 
								session.handleRootSize(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case "resize":
 | 
							case "resize":
 | 
				
			||||||
			session.handleResize(data)
 | 
								session.handleResize(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -291,7 +288,8 @@ func (app *application) startSession(params DataObject, events chan DataObject,
 | 
				
			||||||
// NewApplication - create the new application and start it
 | 
					// NewApplication - create the new application and start it
 | 
				
			||||||
func StartApp(addr string, createContentFunc func(Session) SessionContent, params AppParams) {
 | 
					func StartApp(addr string, createContentFunc func(Session) SessionContent, params AppParams) {
 | 
				
			||||||
	app := new(application)
 | 
						app := new(application)
 | 
				
			||||||
	app.init(params)
 | 
						app.params = params
 | 
				
			||||||
 | 
						app.sessions = map[int]Session{}
 | 
				
			||||||
	app.createContentFunc = createContentFunc
 | 
						app.createContentFunc = createContentFunc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.Handle("/", app)
 | 
						http.Handle("/", app)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,10 +12,11 @@ theme {
 | 
				
			||||||
		ruiHighlightTextColor = #FFFFFFFF,
 | 
							ruiHighlightTextColor = #FFFFFFFF,
 | 
				
			||||||
		ruiSelectedColor = #FFE0E0E0,
 | 
							ruiSelectedColor = #FFE0E0E0,
 | 
				
			||||||
		ruiSelectedTextColor = #FF000000,
 | 
							ruiSelectedTextColor = #FF000000,
 | 
				
			||||||
		ruiPopupBackgroundColor = #FFFFFFFF,
 | 
							ruiPopupBackgroundColor = #FFF5F5F5,
 | 
				
			||||||
		ruiPopupTextColor = #FF000000,
 | 
							ruiPopupTextColor = black,
 | 
				
			||||||
		ruiPopupTitleColor = #FF0000FF,
 | 
							ruiPopupTitleColor = #FF0000FF,
 | 
				
			||||||
		ruiPopupTitleTextColor = #FFFFFFFF,
 | 
							ruiPopupTitleTextColor = #FFFFFFFF,
 | 
				
			||||||
 | 
							ruiPopupShadow = #80808080,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ruiTabBarBackgroundColor = #FFEEEEEE,
 | 
							ruiTabBarBackgroundColor = #FFEEEEEE,
 | 
				
			||||||
		ruiTabColor = #FFD0D0D0,
 | 
							ruiTabColor = #FFD0D0D0,
 | 
				
			||||||
| 
						 | 
					@ -33,6 +34,9 @@ theme {
 | 
				
			||||||
		ruiButtonDisabledTextColor = #FFA0A0A0,
 | 
							ruiButtonDisabledTextColor = #FFA0A0A0,
 | 
				
			||||||
		ruiHighlightColor = #FF1A74E8,
 | 
							ruiHighlightColor = #FF1A74E8,
 | 
				
			||||||
		ruiHighlightTextColor = #FFFFFFFF,
 | 
							ruiHighlightTextColor = #FFFFFFFF,
 | 
				
			||||||
 | 
							ruiPopupBackgroundColor = #FF424242,
 | 
				
			||||||
 | 
							ruiPopupTextColor = white,
 | 
				
			||||||
 | 
							ruiPopupShadow = #80EEEEEE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ruiTabBarBackgroundColor = #FF303030,
 | 
							ruiTabBarBackgroundColor = #FF303030,
 | 
				
			||||||
		ruiTabColor = #FF606060,
 | 
							ruiTabColor = #FF606060,
 | 
				
			||||||
| 
						 | 
					@ -212,7 +216,7 @@ theme {
 | 
				
			||||||
			background-color = @ruiPopupBackgroundColor,
 | 
								background-color = @ruiPopupBackgroundColor,
 | 
				
			||||||
			text-color = @ruiPopupTextColor,
 | 
								text-color = @ruiPopupTextColor,
 | 
				
			||||||
			radius = 4px,
 | 
								radius = 4px,
 | 
				
			||||||
			shadow = _{spread-radius=4px, blur=16px, color=#80808080},
 | 
								shadow = _{spread-radius=4px, blur=16px, color=@ruiPopupShadow },
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ruiPopupTitle {
 | 
							ruiPopupTitle {
 | 
				
			||||||
			background-color = @ruiPopupTitleColor,
 | 
								background-color = @ruiPopupTitleColor,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,17 @@ func (detailsView *detailsViewData) Init(session Session) {
 | 
				
			||||||
	//detailsView.systemClass = "ruiDetailsView"
 | 
						//detailsView.systemClass = "ruiDetailsView"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (detailsView *detailsViewData) Views() []View {
 | 
				
			||||||
 | 
						views := detailsView.viewsContainerData.Views()
 | 
				
			||||||
 | 
						if summary := detailsView.get(Summary); summary != nil {
 | 
				
			||||||
 | 
							switch summary := summary.(type) {
 | 
				
			||||||
 | 
							case View:
 | 
				
			||||||
 | 
								return append([]View{summary}, views...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return views
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (detailsView *detailsViewData) Remove(tag string) {
 | 
					func (detailsView *detailsViewData) Remove(tag string) {
 | 
				
			||||||
	detailsView.remove(strings.ToLower(tag))
 | 
						detailsView.remove(strings.ToLower(tag))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -75,10 +86,12 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case View:
 | 
							case View:
 | 
				
			||||||
			detailsView.properties[Summary] = value
 | 
								detailsView.properties[Summary] = value
 | 
				
			||||||
 | 
								value.setParentID(detailsView.htmlID())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case DataObject:
 | 
							case DataObject:
 | 
				
			||||||
			if view := CreateViewFromObject(detailsView.Session(), value); view != nil {
 | 
								if view := CreateViewFromObject(detailsView.Session(), value); view != nil {
 | 
				
			||||||
				detailsView.properties[Summary] = view
 | 
									detailsView.properties[Summary] = view
 | 
				
			||||||
 | 
									view.setParentID(detailsView.htmlID())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return false
 | 
									return false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								init.go
								
								
								
								
							
							
						
						
									
										6
									
								
								init.go
								
								
								
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package rui
 | 
					package rui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	//resources.init()
 | 
						if theme, ok := CreateThemeFromText(defaultThemeText); ok {
 | 
				
			||||||
	defaultTheme.init()
 | 
							defaultTheme = theme
 | 
				
			||||||
	defaultTheme.addText(defaultThemeText)
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										52
									
								
								resources.go
								
								
								
								
							
							
						
						
									
										52
									
								
								resources.go
								
								
								
								
							| 
						 | 
					@ -32,7 +32,7 @@ type imagePath struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type resourceManager struct {
 | 
					type resourceManager struct {
 | 
				
			||||||
	embedFS      []*embed.FS
 | 
						embedFS      []*embed.FS
 | 
				
			||||||
	themes       map[string]*theme
 | 
						themes       map[string]Theme
 | 
				
			||||||
	images       map[string]imagePath
 | 
						images       map[string]imagePath
 | 
				
			||||||
	imageSrcSets map[string][]scaledImage
 | 
						imageSrcSets map[string][]scaledImage
 | 
				
			||||||
	path         string
 | 
						path         string
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,7 @@ type resourceManager struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var resources = resourceManager{
 | 
					var resources = resourceManager{
 | 
				
			||||||
	embedFS:      []*embed.FS{},
 | 
						embedFS:      []*embed.FS{},
 | 
				
			||||||
	themes:       map[string]*theme{},
 | 
						themes:       map[string]Theme{},
 | 
				
			||||||
	images:       map[string]imagePath{},
 | 
						images:       map[string]imagePath{},
 | 
				
			||||||
	imageSrcSets: map[string][]scaledImage{},
 | 
						imageSrcSets: map[string][]scaledImage{},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -107,7 +107,7 @@ func scanEmbedThemesDir(fs *embed.FS, dir string) {
 | 
				
			||||||
				scanEmbedThemesDir(fs, path)
 | 
									scanEmbedThemesDir(fs, path)
 | 
				
			||||||
			} else if strings.ToLower(filepath.Ext(name)) == ".rui" {
 | 
								} else if strings.ToLower(filepath.Ext(name)) == ".rui" {
 | 
				
			||||||
				if data, err := fs.ReadFile(path); err == nil {
 | 
									if data, err := fs.ReadFile(path); err == nil {
 | 
				
			||||||
					RegisterThemeText(string(data))
 | 
										registerThemeText(string(data))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -198,7 +198,7 @@ func scanThemesDir(path string) {
 | 
				
			||||||
					scanThemesDir(newPath)
 | 
										scanThemesDir(newPath)
 | 
				
			||||||
				} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" {
 | 
									} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" {
 | 
				
			||||||
					if data, err := ioutil.ReadFile(newPath); err == nil {
 | 
										if data, err := ioutil.ReadFile(newPath); err == nil {
 | 
				
			||||||
						RegisterThemeText(string(data))
 | 
											registerThemeText(string(data))
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						ErrorLog(err.Error())
 | 
											ErrorLog(err.Error())
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					@ -223,32 +223,19 @@ func SetResourcePath(path string) {
 | 
				
			||||||
	scanStringsDir(resources.path + stringsDir)
 | 
						scanStringsDir(resources.path + stringsDir)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegisterThemeText parse text and add result to the theme list
 | 
					func registerThemeText(text string) bool {
 | 
				
			||||||
func RegisterThemeText(text string) bool {
 | 
						theme, ok := CreateThemeFromText(text)
 | 
				
			||||||
	data := ParseDataText(text)
 | 
						if !ok {
 | 
				
			||||||
	if data == nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !data.IsObject() {
 | 
						name := theme.Name()
 | 
				
			||||||
		ErrorLog(`Root element is not object`)
 | 
						if name == "" {
 | 
				
			||||||
		return false
 | 
							defaultTheme.concat(theme)
 | 
				
			||||||
	}
 | 
						} else if t, ok := resources.themes[name]; ok {
 | 
				
			||||||
	if data.Tag() != "theme" {
 | 
							t.concat(theme)
 | 
				
			||||||
		ErrorLog(`Invalid the root object tag. Must be "theme"`)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if name, ok := data.PropertyValue("name"); ok && name != "" {
 | 
					 | 
				
			||||||
		t := resources.themes[name]
 | 
					 | 
				
			||||||
		if t == nil {
 | 
					 | 
				
			||||||
			t = new(theme)
 | 
					 | 
				
			||||||
			t.init()
 | 
					 | 
				
			||||||
			resources.themes[name] = t
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		t.addData(data)
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		defaultTheme.addData(data)
 | 
							resources.themes[name] = theme
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
| 
						 | 
					@ -426,3 +413,16 @@ func AllImageResources() []string {
 | 
				
			||||||
	sort.Strings(result)
 | 
						sort.Strings(result)
 | 
				
			||||||
	return result
 | 
						return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AddTheme(theme Theme) {
 | 
				
			||||||
 | 
						if theme != nil {
 | 
				
			||||||
 | 
							name := theme.Name()
 | 
				
			||||||
 | 
							if name == "" {
 | 
				
			||||||
 | 
								defaultTheme.concat(theme)
 | 
				
			||||||
 | 
							} else if t, ok := resources.themes[name]; ok {
 | 
				
			||||||
 | 
								t.concat(theme)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								resources.themes[name] = theme
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										55
									
								
								session.go
								
								
								
								
							
							
						
						
									
										55
									
								
								session.go
								
								
								
								
							| 
						 | 
					@ -94,6 +94,7 @@ type Session interface {
 | 
				
			||||||
	runScript(script string)
 | 
						runScript(script string)
 | 
				
			||||||
	runGetterScript(script string) DataObject //, answer chan DataObject)
 | 
						runGetterScript(script string) DataObject //, answer chan DataObject)
 | 
				
			||||||
	handleAnswer(data DataObject)
 | 
						handleAnswer(data DataObject)
 | 
				
			||||||
 | 
						handleRootSize(data DataObject)
 | 
				
			||||||
	handleResize(data DataObject)
 | 
						handleResize(data DataObject)
 | 
				
			||||||
	handleViewEvent(command string, data DataObject)
 | 
						handleViewEvent(command string, data DataObject)
 | 
				
			||||||
	close()
 | 
						close()
 | 
				
			||||||
| 
						 | 
					@ -113,10 +114,12 @@ type Session interface {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type sessionData struct {
 | 
					type sessionData struct {
 | 
				
			||||||
	customTheme      *theme
 | 
						customTheme      Theme
 | 
				
			||||||
	currentTheme     *theme
 | 
						currentTheme     Theme
 | 
				
			||||||
	darkTheme        bool
 | 
						darkTheme        bool
 | 
				
			||||||
	touchScreen      bool
 | 
						touchScreen      bool
 | 
				
			||||||
 | 
						screenWidth      int
 | 
				
			||||||
 | 
						screenHeight     int
 | 
				
			||||||
	textDirection    int
 | 
						textDirection    int
 | 
				
			||||||
	pixelRatio       float64
 | 
						pixelRatio       float64
 | 
				
			||||||
	userAgent        string
 | 
						userAgent        string
 | 
				
			||||||
| 
						 | 
					@ -137,6 +140,7 @@ type sessionData struct {
 | 
				
			||||||
	brige            WebBrige
 | 
						brige            WebBrige
 | 
				
			||||||
	events           chan DataObject
 | 
						events           chan DataObject
 | 
				
			||||||
	animationCounter int
 | 
						animationCounter int
 | 
				
			||||||
 | 
						animationCSS     string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newSession(app Application, id int, customTheme string, params DataObject) Session {
 | 
					func newSession(app Application, id int, customTheme string, params DataObject) Session {
 | 
				
			||||||
| 
						 | 
					@ -151,9 +155,10 @@ func newSession(app Application, id int, customTheme string, params DataObject)
 | 
				
			||||||
	session.viewCounter = 0
 | 
						session.viewCounter = 0
 | 
				
			||||||
	session.ignoreUpdates = false
 | 
						session.ignoreUpdates = false
 | 
				
			||||||
	session.animationCounter = 0
 | 
						session.animationCounter = 0
 | 
				
			||||||
 | 
						session.animationCSS = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if customTheme != "" {
 | 
						if customTheme != "" {
 | 
				
			||||||
		if theme, ok := newTheme(customTheme); ok {
 | 
							if theme, ok := CreateThemeFromText(customTheme); ok {
 | 
				
			||||||
			session.customTheme = theme
 | 
								session.customTheme = theme
 | 
				
			||||||
			session.currentTheme = nil
 | 
								session.currentTheme = nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -216,26 +221,24 @@ func (session *sessionData) close() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) styleProperty(styleTag, propertyTag string) (string, bool) {
 | 
					func (session *sessionData) styleProperty(styleTag, propertyTag string) (string, bool) {
 | 
				
			||||||
	if style, ok := session.getCurrentTheme().styles[styleTag]; ok {
 | 
						style := session.getCurrentTheme().style(styleTag)
 | 
				
			||||||
	if value, ok := style[propertyTag]; ok {
 | 
						if value, ok := style[propertyTag]; ok {
 | 
				
			||||||
		if text, ok := value.(string); ok {
 | 
							if text, ok := value.(string); ok {
 | 
				
			||||||
			return session.resolveConstants(text)
 | 
								return session.resolveConstants(text)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//errorLogF(`property "%v" not found`, propertyTag)
 | 
						//errorLogF(`property "%v" not found`, propertyTag)
 | 
				
			||||||
	return "", false
 | 
						return "", false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) stylePropertyNode(styleTag, propertyTag string) DataNode {
 | 
					func (session *sessionData) stylePropertyNode(styleTag, propertyTag string) DataNode {
 | 
				
			||||||
	if style, ok := session.getCurrentTheme().styles[styleTag]; ok {
 | 
						style := session.getCurrentTheme().style(styleTag)
 | 
				
			||||||
	if value, ok := style[propertyTag]; ok {
 | 
						if value, ok := style[propertyTag]; ok {
 | 
				
			||||||
		if node, ok := value.(DataNode); ok {
 | 
							if node, ok := value.(DataNode); ok {
 | 
				
			||||||
			return node
 | 
								return node
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -279,6 +282,8 @@ func (session *sessionData) RootView() View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) writeInitScript(writer *strings.Builder) {
 | 
					func (session *sessionData) writeInitScript(writer *strings.Builder) {
 | 
				
			||||||
	if css := session.getCurrentTheme().cssText(session); css != "" {
 | 
						if css := session.getCurrentTheme().cssText(session); css != "" {
 | 
				
			||||||
 | 
							css = strings.ReplaceAll(css, "\n", `\n`)
 | 
				
			||||||
 | 
							css = strings.ReplaceAll(css, "\t", `\t`)
 | 
				
			||||||
		writer.WriteString(`document.querySelector('style').textContent += "`)
 | 
							writer.WriteString(`document.querySelector('style').textContent += "`)
 | 
				
			||||||
		writer.WriteString(css)
 | 
							writer.WriteString(css)
 | 
				
			||||||
		writer.WriteString("\";\n")
 | 
							writer.WriteString("\";\n")
 | 
				
			||||||
| 
						 | 
					@ -295,7 +300,19 @@ func (session *sessionData) reload() {
 | 
				
			||||||
	buffer := allocStringBuilder()
 | 
						buffer := allocStringBuilder()
 | 
				
			||||||
	defer freeStringBuilder(buffer)
 | 
						defer freeStringBuilder(buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session.writeInitScript(buffer)
 | 
						css := appStyles + session.getCurrentTheme().cssText(session) + session.animationCSS
 | 
				
			||||||
 | 
						css = strings.ReplaceAll(css, "\n", `\n`)
 | 
				
			||||||
 | 
						css = strings.ReplaceAll(css, "\t", `\t`)
 | 
				
			||||||
 | 
						buffer.WriteString(`document.querySelector('style').textContent = "`)
 | 
				
			||||||
 | 
						buffer.WriteString(css)
 | 
				
			||||||
 | 
						buffer.WriteString("\";\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if session.rootView != nil {
 | 
				
			||||||
 | 
							buffer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
 | 
				
			||||||
 | 
							viewHTML(session.rootView, buffer)
 | 
				
			||||||
 | 
							buffer.WriteString("';\nscanElementsSize();")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session.runScript(buffer.String())
 | 
						session.runScript(buffer.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -360,6 +377,28 @@ func (session *sessionData) handleAnswer(data DataObject) {
 | 
				
			||||||
	session.brige.AnswerReceived(data)
 | 
						session.brige.AnswerReceived(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (session *sessionData) handleRootSize(data DataObject) {
 | 
				
			||||||
 | 
						getValue := func(tag string) int {
 | 
				
			||||||
 | 
							if value, ok := data.PropertyValue(tag); ok {
 | 
				
			||||||
 | 
								float, err := strconv.ParseFloat(value, 64)
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									return int(float)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ErrorLog(`Resize event error: ` + err.Error())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ErrorLogF(`Resize event error: the property "%s" not found`, tag)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if w := getValue("width"); w > 0 {
 | 
				
			||||||
 | 
							session.screenWidth = w
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if h := getValue("height"); h > 0 {
 | 
				
			||||||
 | 
							session.screenHeight = h
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) handleResize(data DataObject) {
 | 
					func (session *sessionData) handleResize(data DataObject) {
 | 
				
			||||||
	if node := data.PropertyWithTag("views"); node != nil && node.Type() == ArrayNode {
 | 
						if node := data.PropertyWithTag("views"); node != nil && node.Type() == ArrayNode {
 | 
				
			||||||
		for _, el := range node.ArrayElements() {
 | 
							for _, el := range node.ArrayElements() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										106
									
								
								sessionTheme.go
								
								
								
								
							
							
						
						
									
										106
									
								
								sessionTheme.go
								
								
								
								
							| 
						 | 
					@ -2,7 +2,6 @@ package rui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,21 +23,10 @@ func (session *sessionData) TextDirection() int {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) constant(tag string, prevTags []string) (string, bool) {
 | 
					func (session *sessionData) constant(tag string, prevTags []string) (string, bool) {
 | 
				
			||||||
	tags := append(prevTags, tag)
 | 
						tags := append(prevTags, tag)
 | 
				
			||||||
	result := ""
 | 
					 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						theme := session.getCurrentTheme()
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		ok := false
 | 
							result := theme.constant(tag, session.touchScreen)
 | 
				
			||||||
		if session.touchScreen {
 | 
							if result == "" {
 | 
				
			||||||
			if theme.touchConstants != nil {
 | 
					 | 
				
			||||||
				result, ok = theme.touchConstants[tag]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			result, ok = theme.constants[tag]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			ErrorLogF(`"%v" constant not found`, tag)
 | 
								ErrorLogF(`"%v" constant not found`, tag)
 | 
				
			||||||
			return "", false
 | 
								return "", false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -49,8 +37,7 @@ func (session *sessionData) constant(tag string, prevTags []string) (string, boo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, separator := range []string{",", " ", ":", ";", "|", "/"} {
 | 
							for _, separator := range []string{",", " ", ":", ";", "|", "/"} {
 | 
				
			||||||
			if strings.Contains(result, separator) {
 | 
								if strings.Contains(result, separator) {
 | 
				
			||||||
				result, ok = session.resolveConstantsNext(result, tags)
 | 
									return session.resolveConstantsNext(result, tags)
 | 
				
			||||||
				return result, ok
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,14 +112,13 @@ func (session *sessionData) Constant(tag string) (string, bool) {
 | 
				
			||||||
	return session.constant(tag, []string{})
 | 
						return session.constant(tag, []string{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) getCurrentTheme() *theme {
 | 
					func (session *sessionData) getCurrentTheme() Theme {
 | 
				
			||||||
	if session.currentTheme != nil {
 | 
						if session.currentTheme != nil {
 | 
				
			||||||
		return session.currentTheme
 | 
							return session.currentTheme
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if session.customTheme != nil {
 | 
						if session.customTheme != nil {
 | 
				
			||||||
		session.currentTheme = new(theme)
 | 
							session.currentTheme = NewTheme("")
 | 
				
			||||||
		session.currentTheme.init()
 | 
					 | 
				
			||||||
		session.currentTheme.concat(defaultTheme)
 | 
							session.currentTheme.concat(defaultTheme)
 | 
				
			||||||
		session.currentTheme.concat(session.customTheme)
 | 
							session.currentTheme.concat(session.customTheme)
 | 
				
			||||||
		return session.currentTheme
 | 
							return session.currentTheme
 | 
				
			||||||
| 
						 | 
					@ -144,23 +130,10 @@ func (session *sessionData) getCurrentTheme() *theme {
 | 
				
			||||||
// Color return the color with "tag" name or 0 if it is not exists
 | 
					// Color return the color with "tag" name or 0 if it is not exists
 | 
				
			||||||
func (session *sessionData) Color(tag string) (Color, bool) {
 | 
					func (session *sessionData) Color(tag string) (Color, bool) {
 | 
				
			||||||
	tags := []string{tag}
 | 
						tags := []string{tag}
 | 
				
			||||||
	result := ""
 | 
					 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						theme := session.getCurrentTheme()
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		ok := false
 | 
							result := theme.color(tag, session.darkTheme)
 | 
				
			||||||
		if session.darkTheme {
 | 
							if result == "" {
 | 
				
			||||||
			if theme.darkColors != nil {
 | 
					 | 
				
			||||||
				result, ok = theme.darkColors[tag]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			if theme.colors != nil {
 | 
					 | 
				
			||||||
				result, ok = theme.colors[tag]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			ErrorLogF(`"%v" color not found`, tag)
 | 
								ErrorLogF(`"%v" color not found`, tag)
 | 
				
			||||||
			return 0, false
 | 
								return 0, false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -188,23 +161,10 @@ func (session *sessionData) Color(tag string) (Color, bool) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) ImageConstant(tag string) (string, bool) {
 | 
					func (session *sessionData) ImageConstant(tag string) (string, bool) {
 | 
				
			||||||
	tags := []string{tag}
 | 
						tags := []string{tag}
 | 
				
			||||||
	result := ""
 | 
					 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						theme := session.getCurrentTheme()
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		ok := false
 | 
							result := theme.image(tag, session.darkTheme)
 | 
				
			||||||
		if session.darkTheme {
 | 
							if result == "" {
 | 
				
			||||||
			if theme.darkImages != nil {
 | 
					 | 
				
			||||||
				result, ok = theme.darkImages[tag]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			if theme.images != nil {
 | 
					 | 
				
			||||||
				result, ok = theme.images[tag]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			ErrorLogF(`"%v" image not found`, tag)
 | 
								ErrorLogF(`"%v" image not found`, tag)
 | 
				
			||||||
			return "", false
 | 
								return "", false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -379,55 +339,13 @@ func (session *sessionData) SetLanguage(lang string) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) ConstantTags() []string {
 | 
					func (session *sessionData) ConstantTags() []string {
 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						return session.getCurrentTheme().ConstantTags()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	keys := make([]string, 0, len(theme.constants))
 | 
					 | 
				
			||||||
	for k := range theme.constants {
 | 
					 | 
				
			||||||
		keys = append(keys, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for tag := range theme.touchConstants {
 | 
					 | 
				
			||||||
		if _, ok := theme.constants[tag]; !ok {
 | 
					 | 
				
			||||||
			keys = append(keys, tag)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sort.Strings(keys)
 | 
					 | 
				
			||||||
	return keys
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) ColorTags() []string {
 | 
					func (session *sessionData) ColorTags() []string {
 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						return session.getCurrentTheme().ColorTags()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	keys := make([]string, 0, len(theme.colors))
 | 
					 | 
				
			||||||
	for k := range theme.colors {
 | 
					 | 
				
			||||||
		keys = append(keys, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for tag := range theme.darkColors {
 | 
					 | 
				
			||||||
		if _, ok := theme.colors[tag]; !ok {
 | 
					 | 
				
			||||||
			keys = append(keys, tag)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sort.Strings(keys)
 | 
					 | 
				
			||||||
	return keys
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *sessionData) ImageConstantTags() []string {
 | 
					func (session *sessionData) ImageConstantTags() []string {
 | 
				
			||||||
	theme := session.getCurrentTheme()
 | 
						return session.getCurrentTheme().ImageConstantTags()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	keys := make([]string, 0, len(theme.colors))
 | 
					 | 
				
			||||||
	for k := range theme.images {
 | 
					 | 
				
			||||||
		keys = append(keys, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for tag := range theme.darkImages {
 | 
					 | 
				
			||||||
		if _, ok := theme.images[tag]; !ok {
 | 
					 | 
				
			||||||
			keys = append(keys, tag)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sort.Strings(keys)
 | 
					 | 
				
			||||||
	return keys
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										329
									
								
								theme.go
								
								
								
								
							
							
						
						
									
										329
									
								
								theme.go
								
								
								
								
							| 
						 | 
					@ -7,63 +7,105 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	defaultMedia   = 0
 | 
						DefaultMedia   = 0
 | 
				
			||||||
	portraitMedia  = 1
 | 
						PortraitMedia  = 1
 | 
				
			||||||
	landscapeMedia = 2
 | 
						LandscapeMedia = 2
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type mediaStyle struct {
 | 
					type MediaStyle struct {
 | 
				
			||||||
	orientation int
 | 
						Orientation int
 | 
				
			||||||
	width       int
 | 
						MaxWidth    int
 | 
				
			||||||
	height      int
 | 
						MaxHeight   int
 | 
				
			||||||
	styles      map[string]Params
 | 
						Styles      map[string]Params
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (rule mediaStyle) cssText() string {
 | 
					type theme struct {
 | 
				
			||||||
 | 
						name           string
 | 
				
			||||||
 | 
						constants      map[string]string
 | 
				
			||||||
 | 
						touchConstants map[string]string
 | 
				
			||||||
 | 
						colors         map[string]string
 | 
				
			||||||
 | 
						darkColors     map[string]string
 | 
				
			||||||
 | 
						images         map[string]string
 | 
				
			||||||
 | 
						darkImages     map[string]string
 | 
				
			||||||
 | 
						styles         map[string]Params
 | 
				
			||||||
 | 
						mediaStyles    []MediaStyle
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Theme interface {
 | 
				
			||||||
 | 
						Name() string
 | 
				
			||||||
 | 
						Constant(tag string) (string, string)
 | 
				
			||||||
 | 
						SetConstant(tag string, value, touchUIValue string)
 | 
				
			||||||
 | 
						// ConstantTags returns the list of all available constants
 | 
				
			||||||
 | 
						ConstantTags() []string
 | 
				
			||||||
 | 
						Color(tag string) (string, string)
 | 
				
			||||||
 | 
						SetColor(tag, color, darkUIColor string)
 | 
				
			||||||
 | 
						// ColorTags returns the list of all available color constants
 | 
				
			||||||
 | 
						ColorTags() []string
 | 
				
			||||||
 | 
						Image(tag string) (string, string)
 | 
				
			||||||
 | 
						SetImage(tag, image, darkUIImage string)
 | 
				
			||||||
 | 
						// ImageConstantTags returns the list of all available image constants
 | 
				
			||||||
 | 
						ImageConstantTags() []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constant(tag string, touchUI bool) string
 | 
				
			||||||
 | 
						color(tag string, darkUI bool) string
 | 
				
			||||||
 | 
						image(tag string, darkUI bool) string
 | 
				
			||||||
 | 
						style(tag string) Params
 | 
				
			||||||
 | 
						concat(anotherTheme Theme)
 | 
				
			||||||
 | 
						cssText(session Session) string
 | 
				
			||||||
 | 
						data() *theme
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (rule MediaStyle) cssText() string {
 | 
				
			||||||
	builder := allocStringBuilder()
 | 
						builder := allocStringBuilder()
 | 
				
			||||||
	defer freeStringBuilder(builder)
 | 
						defer freeStringBuilder(builder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch rule.orientation {
 | 
						switch rule.Orientation {
 | 
				
			||||||
	case portraitMedia:
 | 
						case PortraitMedia:
 | 
				
			||||||
		builder.WriteString(" and (orientation: portrait)")
 | 
							builder.WriteString(" and (orientation: portrait)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case landscapeMedia:
 | 
						case LandscapeMedia:
 | 
				
			||||||
		builder.WriteString(" and (orientation: landscape)")
 | 
							builder.WriteString(" and (orientation: landscape)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if rule.width > 0 {
 | 
						if rule.MaxWidth > 0 {
 | 
				
			||||||
		builder.WriteString(" and (max-width: ")
 | 
							builder.WriteString(" and (max-width: ")
 | 
				
			||||||
		builder.WriteString(strconv.Itoa(rule.width))
 | 
							builder.WriteString(strconv.Itoa(rule.MaxWidth))
 | 
				
			||||||
		builder.WriteString("px)")
 | 
							builder.WriteString("px)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if rule.height > 0 {
 | 
						if rule.MaxHeight > 0 {
 | 
				
			||||||
		builder.WriteString(" and (max-height: ")
 | 
							builder.WriteString(" and (max-height: ")
 | 
				
			||||||
		builder.WriteString(strconv.Itoa(rule.height))
 | 
							builder.WriteString(strconv.Itoa(rule.MaxHeight))
 | 
				
			||||||
		builder.WriteString("px)")
 | 
							builder.WriteString("px)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return builder.String()
 | 
						return builder.String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseMediaRule(text string) (mediaStyle, bool) {
 | 
					func parseMediaRule(text string) (MediaStyle, bool) {
 | 
				
			||||||
	rule := mediaStyle{orientation: defaultMedia, width: 0, height: 0, styles: map[string]Params{}}
 | 
						rule := MediaStyle{
 | 
				
			||||||
 | 
							Orientation: DefaultMedia,
 | 
				
			||||||
 | 
							MaxWidth:    0,
 | 
				
			||||||
 | 
							MaxHeight:   0,
 | 
				
			||||||
 | 
							Styles:      map[string]Params{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elements := strings.Split(text, ":")
 | 
						elements := strings.Split(text, ":")
 | 
				
			||||||
	for i := 1; i < len(elements); i++ {
 | 
						for i := 1; i < len(elements); i++ {
 | 
				
			||||||
		switch element := elements[i]; element {
 | 
							switch element := elements[i]; element {
 | 
				
			||||||
		case "portrait":
 | 
							case "portrait":
 | 
				
			||||||
			if rule.orientation != defaultMedia {
 | 
								if rule.Orientation != DefaultMedia {
 | 
				
			||||||
				ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
 | 
									ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
 | 
				
			||||||
				return rule, false
 | 
									return rule, false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			rule.orientation = portraitMedia
 | 
								rule.Orientation = PortraitMedia
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case "landscape":
 | 
							case "landscape":
 | 
				
			||||||
			if rule.orientation != defaultMedia {
 | 
								if rule.Orientation != DefaultMedia {
 | 
				
			||||||
				ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
 | 
									ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
 | 
				
			||||||
				return rule, false
 | 
									return rule, false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			rule.orientation = landscapeMedia
 | 
								rule.Orientation = LandscapeMedia
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			elementSize := func(name string) (int, bool) {
 | 
								elementSize := func(name string) (int, bool) {
 | 
				
			||||||
| 
						 | 
					@ -82,20 +124,20 @@ func parseMediaRule(text string) (mediaStyle, bool) {
 | 
				
			||||||
				if !ok {
 | 
									if !ok {
 | 
				
			||||||
					return rule, false
 | 
										return rule, false
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if rule.width != 0 {
 | 
									if rule.MaxWidth != 0 {
 | 
				
			||||||
					ErrorLog(`Duplicate "width" tag in the style section "` + text + `"`)
 | 
										ErrorLog(`Duplicate "width" tag in the style section "` + text + `"`)
 | 
				
			||||||
					return rule, false
 | 
										return rule, false
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				rule.width = size
 | 
									rule.MaxWidth = size
 | 
				
			||||||
			} else if size, ok := elementSize("height"); !ok || size > 0 {
 | 
								} else if size, ok := elementSize("height"); !ok || size > 0 {
 | 
				
			||||||
				if !ok {
 | 
									if !ok {
 | 
				
			||||||
					return rule, false
 | 
										return rule, false
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if rule.height != 0 {
 | 
									if rule.MaxHeight != 0 {
 | 
				
			||||||
					ErrorLog(`Duplicate "height" tag in the style section "` + text + `"`)
 | 
										ErrorLog(`Duplicate "height" tag in the style section "` + text + `"`)
 | 
				
			||||||
					return rule, false
 | 
										return rule, false
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				rule.height = size
 | 
									rule.MaxHeight = size
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ErrorLogF(`Unknown elemnet "%s" in the style section name "%s"`, element, text)
 | 
									ErrorLogF(`Unknown elemnet "%s" in the style section name "%s"`, element, text)
 | 
				
			||||||
				return rule, false
 | 
									return rule, false
 | 
				
			||||||
| 
						 | 
					@ -105,21 +147,16 @@ func parseMediaRule(text string) (mediaStyle, bool) {
 | 
				
			||||||
	return rule, true
 | 
						return rule, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type theme struct {
 | 
					var defaultTheme = NewTheme("")
 | 
				
			||||||
	name           string
 | 
					
 | 
				
			||||||
	constants      map[string]string
 | 
					func NewTheme(name string) Theme {
 | 
				
			||||||
	touchConstants map[string]string
 | 
						result := new(theme)
 | 
				
			||||||
	colors         map[string]string
 | 
						result.init()
 | 
				
			||||||
	darkColors     map[string]string
 | 
						result.name = name
 | 
				
			||||||
	images         map[string]string
 | 
						return result
 | 
				
			||||||
	darkImages     map[string]string
 | 
					 | 
				
			||||||
	styles         map[string]Params
 | 
					 | 
				
			||||||
	mediaStyles    []mediaStyle
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var defaultTheme = new(theme)
 | 
					func CreateThemeFromText(text string) (Theme, bool) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
func newTheme(text string) (*theme, bool) {
 | 
					 | 
				
			||||||
	result := new(theme)
 | 
						result := new(theme)
 | 
				
			||||||
	result.init()
 | 
						result.init()
 | 
				
			||||||
	ok := result.addText(text)
 | 
						ok := result.addText(text)
 | 
				
			||||||
| 
						 | 
					@ -134,50 +171,167 @@ func (theme *theme) init() {
 | 
				
			||||||
	theme.images = map[string]string{}
 | 
						theme.images = map[string]string{}
 | 
				
			||||||
	theme.darkImages = map[string]string{}
 | 
						theme.darkImages = map[string]string{}
 | 
				
			||||||
	theme.styles = map[string]Params{}
 | 
						theme.styles = map[string]Params{}
 | 
				
			||||||
	theme.mediaStyles = []mediaStyle{}
 | 
						theme.mediaStyles = []MediaStyle{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (theme *theme) concat(anotherTheme *theme) {
 | 
					func (theme *theme) Name() string {
 | 
				
			||||||
 | 
						return theme.name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) Constant(tag string) (string, string) {
 | 
				
			||||||
 | 
						return theme.constants[tag], theme.touchConstants[tag]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) SetConstant(tag, value, touchUIValue string) {
 | 
				
			||||||
 | 
						value = strings.Trim(value, " \t")
 | 
				
			||||||
 | 
						if value == "" {
 | 
				
			||||||
 | 
							delete(theme.constants, tag)
 | 
				
			||||||
 | 
							delete(theme.touchConstants, tag)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							theme.constants[tag] = value
 | 
				
			||||||
 | 
							touchUIValue = strings.Trim(touchUIValue, " \t")
 | 
				
			||||||
 | 
							if touchUIValue == "" {
 | 
				
			||||||
 | 
								delete(theme.touchConstants, tag)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								theme.touchConstants[tag] = touchUIValue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) Color(tag string) (string, string) {
 | 
				
			||||||
 | 
						return theme.colors[tag], theme.darkColors[tag]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) SetColor(tag, color, darkUIColor string) {
 | 
				
			||||||
 | 
						color = strings.Trim(color, " \t")
 | 
				
			||||||
 | 
						if color == "" {
 | 
				
			||||||
 | 
							delete(theme.colors, tag)
 | 
				
			||||||
 | 
							delete(theme.darkColors, tag)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							theme.colors[tag] = color
 | 
				
			||||||
 | 
							darkUIColor = strings.Trim(darkUIColor, " \t")
 | 
				
			||||||
 | 
							if darkUIColor == "" {
 | 
				
			||||||
 | 
								delete(theme.darkColors, tag)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								theme.darkColors[tag] = darkUIColor
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) Image(tag string) (string, string) {
 | 
				
			||||||
 | 
						return theme.images[tag], theme.darkImages[tag]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) SetImage(tag, image, darkUIImage string) {
 | 
				
			||||||
 | 
						image = strings.Trim(image, " \t")
 | 
				
			||||||
 | 
						if image == "" {
 | 
				
			||||||
 | 
							delete(theme.images, tag)
 | 
				
			||||||
 | 
							delete(theme.darkImages, tag)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							theme.images[tag] = image
 | 
				
			||||||
 | 
							darkUIImage = strings.Trim(darkUIImage, " \t")
 | 
				
			||||||
 | 
							if darkUIImage == "" {
 | 
				
			||||||
 | 
								delete(theme.darkImages, tag)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								theme.darkImages[tag] = darkUIImage
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) ConstantTags() []string {
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(theme.constants))
 | 
				
			||||||
 | 
						for k := range theme.constants {
 | 
				
			||||||
 | 
							keys = append(keys, k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for tag := range theme.touchConstants {
 | 
				
			||||||
 | 
							if _, ok := theme.constants[tag]; !ok {
 | 
				
			||||||
 | 
								keys = append(keys, tag)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.Strings(keys)
 | 
				
			||||||
 | 
						return keys
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) ColorTags() []string {
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(theme.colors))
 | 
				
			||||||
 | 
						for k := range theme.colors {
 | 
				
			||||||
 | 
							keys = append(keys, k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for tag := range theme.darkColors {
 | 
				
			||||||
 | 
							if _, ok := theme.colors[tag]; !ok {
 | 
				
			||||||
 | 
								keys = append(keys, tag)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.Strings(keys)
 | 
				
			||||||
 | 
						return keys
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) ImageConstantTags() []string {
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(theme.colors))
 | 
				
			||||||
 | 
						for k := range theme.images {
 | 
				
			||||||
 | 
							keys = append(keys, k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for tag := range theme.darkImages {
 | 
				
			||||||
 | 
							if _, ok := theme.images[tag]; !ok {
 | 
				
			||||||
 | 
								keys = append(keys, tag)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.Strings(keys)
 | 
				
			||||||
 | 
						return keys
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) data() *theme {
 | 
				
			||||||
 | 
						return theme
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) concat(anotherTheme Theme) {
 | 
				
			||||||
	if theme.constants == nil {
 | 
						if theme.constants == nil {
 | 
				
			||||||
		theme.init()
 | 
							theme.init()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, constant := range anotherTheme.constants {
 | 
						another := anotherTheme.data()
 | 
				
			||||||
 | 
						for tag, constant := range another.constants {
 | 
				
			||||||
		theme.constants[tag] = constant
 | 
							theme.constants[tag] = constant
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, constant := range anotherTheme.touchConstants {
 | 
						for tag, constant := range another.touchConstants {
 | 
				
			||||||
		theme.touchConstants[tag] = constant
 | 
							theme.touchConstants[tag] = constant
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, color := range anotherTheme.colors {
 | 
						for tag, color := range another.colors {
 | 
				
			||||||
		theme.colors[tag] = color
 | 
							theme.colors[tag] = color
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, color := range anotherTheme.darkColors {
 | 
						for tag, color := range another.darkColors {
 | 
				
			||||||
		theme.darkColors[tag] = color
 | 
							theme.darkColors[tag] = color
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, image := range anotherTheme.images {
 | 
						for tag, image := range another.images {
 | 
				
			||||||
		theme.images[tag] = image
 | 
							theme.images[tag] = image
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, image := range anotherTheme.darkImages {
 | 
						for tag, image := range another.darkImages {
 | 
				
			||||||
		theme.darkImages[tag] = image
 | 
							theme.darkImages[tag] = image
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for tag, style := range anotherTheme.styles {
 | 
						for tag, style := range another.styles {
 | 
				
			||||||
		theme.styles[tag] = style
 | 
							theme.styles[tag] = style
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, anotherMedia := range anotherTheme.mediaStyles {
 | 
						for _, anotherMedia := range another.mediaStyles {
 | 
				
			||||||
		exists := false
 | 
							exists := false
 | 
				
			||||||
		for _, media := range theme.mediaStyles {
 | 
							for _, media := range theme.mediaStyles {
 | 
				
			||||||
			if anotherMedia.height == media.height &&
 | 
								if anotherMedia.MaxHeight == media.MaxHeight &&
 | 
				
			||||||
				anotherMedia.width == media.width &&
 | 
									anotherMedia.MaxWidth == media.MaxWidth &&
 | 
				
			||||||
				anotherMedia.orientation == media.orientation {
 | 
									anotherMedia.Orientation == media.Orientation {
 | 
				
			||||||
				for tag, style := range anotherMedia.styles {
 | 
									for tag, style := range anotherMedia.Styles {
 | 
				
			||||||
					media.styles[tag] = style
 | 
										media.Styles[tag] = style
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				exists = true
 | 
									exists = true
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
| 
						 | 
					@ -211,7 +365,7 @@ func (theme *theme) cssText(session Session) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, media := range theme.mediaStyles {
 | 
						for _, media := range theme.mediaStyles {
 | 
				
			||||||
		builder.startMedia(media.cssText())
 | 
							builder.startMedia(media.cssText())
 | 
				
			||||||
		for tag, obj := range media.styles {
 | 
							for tag, obj := range media.Styles {
 | 
				
			||||||
			var style viewStyle
 | 
								var style viewStyle
 | 
				
			||||||
			style.init()
 | 
								style.init()
 | 
				
			||||||
			for tag, value := range obj {
 | 
								for tag, value := range obj {
 | 
				
			||||||
| 
						 | 
					@ -363,7 +517,7 @@ func (theme *theme) parseThemeData(data DataObject) {
 | 
				
			||||||
						for k := 0; k < arraySize; k++ {
 | 
											for k := 0; k < arraySize; k++ {
 | 
				
			||||||
							if element := d.ArrayElement(k); element != nil && element.IsObject() {
 | 
												if element := d.ArrayElement(k); element != nil && element.IsObject() {
 | 
				
			||||||
								if obj := element.Object(); obj != nil {
 | 
													if obj := element.Object(); obj != nil {
 | 
				
			||||||
									rule.styles[obj.Tag()] = objToParams(obj)
 | 
														rule.Styles[obj.Tag()] = objToParams(obj)
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
| 
						 | 
					@ -376,13 +530,66 @@ func (theme *theme) parseThemeData(data DataObject) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(theme.mediaStyles) > 0 {
 | 
						if len(theme.mediaStyles) > 0 {
 | 
				
			||||||
		sort.SliceStable(theme.mediaStyles, func(i, j int) bool {
 | 
							sort.SliceStable(theme.mediaStyles, func(i, j int) bool {
 | 
				
			||||||
			if theme.mediaStyles[i].orientation != theme.mediaStyles[j].orientation {
 | 
								if theme.mediaStyles[i].Orientation != theme.mediaStyles[j].Orientation {
 | 
				
			||||||
				return theme.mediaStyles[i].orientation < theme.mediaStyles[j].orientation
 | 
									return theme.mediaStyles[i].Orientation < theme.mediaStyles[j].Orientation
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if theme.mediaStyles[i].width != theme.mediaStyles[j].width {
 | 
								if theme.mediaStyles[i].MaxWidth != theme.mediaStyles[j].MaxWidth {
 | 
				
			||||||
				return theme.mediaStyles[i].width < theme.mediaStyles[j].width
 | 
									return theme.mediaStyles[i].MaxWidth < theme.mediaStyles[j].MaxWidth
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return theme.mediaStyles[i].height < theme.mediaStyles[j].height
 | 
								return theme.mediaStyles[i].MaxHeight < theme.mediaStyles[j].MaxHeight
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) constant(tag string, touchUI bool) string {
 | 
				
			||||||
 | 
						result := ""
 | 
				
			||||||
 | 
						if touchUI {
 | 
				
			||||||
 | 
							if value, ok := theme.touchConstants[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if result == "" {
 | 
				
			||||||
 | 
							if value, ok := theme.constants[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) color(tag string, darkUI bool) string {
 | 
				
			||||||
 | 
						result := ""
 | 
				
			||||||
 | 
						if darkUI {
 | 
				
			||||||
 | 
							if value, ok := theme.darkColors[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if result == "" {
 | 
				
			||||||
 | 
							if value, ok := theme.colors[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) image(tag string, darkUI bool) string {
 | 
				
			||||||
 | 
						result := ""
 | 
				
			||||||
 | 
						if darkUI {
 | 
				
			||||||
 | 
							if value, ok := theme.darkImages[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if result == "" {
 | 
				
			||||||
 | 
							if value, ok := theme.images[tag]; ok {
 | 
				
			||||||
 | 
								result = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (theme *theme) style(tag string) Params {
 | 
				
			||||||
 | 
						if style, ok := theme.styles[tag]; ok {
 | 
				
			||||||
 | 
							return style
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Params{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue