/* Copyright (c) 2020, 2023, The Unified Company. This code is part of Unify. This program is free software; you can redistribute it and/or modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE, as published by the Free Software Foundation. See the GNU AFFERO GENERAL PUBLIC LICENSE, for more details. https://unifyjs.org */ import cssRules from './cssRules.js'; import document from '../unify/document.js'; import cssManager from './cssManager.js'; import tools from '../unify/tools.js'; import definitions from '../unify/definitions.js'; export default class css{ cssCache = new cssManager(); global_css = ""; skipBoxTerms = new Array( "box-sizing", "box-shadow", "box-reflect", "box-decoration-break" ); constructor() { this.boxTermWithoutPrefix = new Array(); for (var i = 0; i < this.skipBoxTerms.length; i++) { var boxTerm = this.skipBoxTerms[i] this.boxTermWithoutPrefix[i] = boxTerm.replace("box-") } } PerformPropertyGridCorrection( property ) { for ( var i = 0; i < this.skipBoxTerms.length; i++ ) { if( this.boxTermWithoutPrefix[ i ].includes( property ) ) { property = this.skipBoxTerms[ i ]; } } return property; } parseObject( object ){ var objectName = object.propertyName; object.__proto__.css = new cssRules(); object.__proto__.boxRules = new cssRules(); object.__proto__.cssRules = new cssRules(); object.cssRules.addStyleListener( "hover", ":hover" ); object.cssRules.addStyleListener( "visited", ":visited" ); object.cssRules.addStyleListener( "link", ":link" ); object.cssRules.addStyleListener( "active", ":active" ); object.cssRules.addStyleListener( "placeholder", "::placeholder" ); object.cssRules.addStyleListener( "scrollbar", "::-webkit-scrollbar" ); object.cssRules.addStyleListener( "scrollbarTrack", "::-webkit-scrollbar-track" ); object.cssRules.addStyleListener( "scrollbarThumb", "::-webkit-scrollbar-thumb" ); object.cssRules.addStyleListener( "scrollbarThumbHover", "::-webkit-scrollbar-thumb:hover" ); } parseCSSValue( value ) { if( typeof value == "number" ) { value += "px"; } return value; } createStyleRule( normalizedProperty, value ){ if( !value ) { return ""; } value = this.parseCSSValue( value ); normalizedProperty = this.PerformPropertyGridCorrection( normalizedProperty ); return "\t" + normalizedProperty + ":" + value + "; \n"; } compareUpperCase( term ) { const compare = function( current ){ if( term.indexOf( current ) != -1 ){ return true; } } const n = definitions.alphabet; var map = n.map( compare ); return map; // [false, false, true, false] where true is where higher case is } hasUpperCase( a ) { let regExp = /[A-Z]/; return regExp.test( a ); } replaceUpperCase( word ) { var map = this.compareUpperCase( word ); var letter = definitions.alphabet[ map.indexOf( true ) ]; return word.replace( letter, "-" + letter.toLowerCase() ); } normalizePropertyName( name ) { return tools.firstLowerCase( name.replace("__", "").replace("box", "") ); } // needs caching normalizeProperty( name ) { // webkit -> -webkit if( name.slice( 0, 6 ) == "webkit" ) { name = "-" + name; } for (var i = 0; i < 6; i++) { if( this.hasUpperCase( name ) ) { name = this.replaceUpperCase( name ); // backgroundColor -> background-color-and-something } } return name; } setter( object, domStyleObject, propertyName, property, value ) { object[ "__" + propertyName ] = value; value = this.parseCSSValue( value ); property = this.PerformPropertyGridCorrection( property ); var NSTabNames = new Array("svg", "circle") // todo : doesnt work properly //if( NSTabNames.includes( object.element.tagName ) ) { // domStyleObject.setAttributeNS( null, property, value ); //} else { domStyleObject.setProperty( property, value ); //} } createSetter( property, objectProperty, object, domStyleObject ) { var css = this; object[ "__" + objectProperty ] = object[ objectProperty ]; object.__defineSetter__( objectProperty, this.setter.bind( this, object, domStyleObject, objectProperty, property ) ); } getter( object, objectProperty, value ) { var newValue = object[ "__" + objectProperty ]; if(typeof newValue == "string") { var chars = newValue.split(); if( chars[chars.length-2] == "p" && chars[chars.length-1] == "x" ) { chars = chars.slice(0, -2); newValue = chars.join(); } } if( tools.isCustomNumber( newValue ) ) { return parseFloat( newValue ); } else { return newValue; } } createGetter( property, objectProperty, object, domStyleObject ) { object.__defineGetter__( objectProperty, this.getter.bind( this, object, objectProperty ) ); } parseProperty( property, object ) { var propertyName = property.name; var propertyValue = property.value; var normalizedProperty = this.normalizeProperty( propertyName ); //if( propertyName == "gridArea" && propertyValue == "none" ) { // return false; //} var slicedProperty = propertyName.toLowerCase().slice( 0, 3 ); if( slicedProperty == "box" ) { object.boxRules.style += this.parseCssTerm( object, propertyName, propertyValue, object.elements[0], "box" ); } else { object.css.style += this.parseCssTerm( object, propertyName, propertyValue, object.element ) } this.parseExtraStyleSuffixes( object, propertyName, propertyValue ); } parseExtraStyleSuffixes( object, propertyName, propertyValue ) { var styleTypes = object.cssRules.styleTypes; for ( var i = 0; i < styleTypes.length; i++ ) { var cssType = styleTypes[i]; var propertyTerm = cssType.propertyTerm; var slicedProperty = propertyName.slice( 0, propertyTerm.length ); if( slicedProperty == propertyTerm && propertyName.length > slicedProperty.length ) { cssType.css += this.parseCssTerm( object, propertyName, propertyValue, object.elements[0], propertyTerm ); } } } parseCssTerm( object, propertyName, propertyValue, element, term = "" ) { var cssPropertyName = tools.firstLowerCase( propertyName.replace( term, "" ) ); var normalizedProperty = this.normalizeProperty( cssPropertyName ); if( this.propertyIsStyle( normalizedProperty ) ) { // Setters and getters don't work yet for special suffixes. if( !term || term == "box" ) { this.createSetter( normalizedProperty, propertyName, object, element.style ); this.createGetter( normalizedProperty, propertyName, object, element.style ); } return this.createStyleRule( normalizedProperty, propertyValue ); } else { return ""; } } clearCache() { this.cssCache.rules = new Array(); } parseCssRule( object, objectPropertyName, propertyName, propertyValue ) { var normalizedProperty = this.normalizeProperty( objectPropertyName ); object.boxRules.style += this.createStyleRule( normalizedProperty, propertyValue ); this.createSetter( objectPropertyName, propertyName, object, object.elements[0].style ); this.createGetter( objectPropertyName, propertyName, object, object.elements[0].style ); } createSelector( className, afterTerm ) { var selector = "." + className; selector += "." + this.device; selector += "." + this.os; selector += "." + this.tint; // this doesnt work yet. if( afterTerm ) { selector += afterTerm; } //selector += "," + selector.replace( "_" + id, "" ); return selector; } createStyleSheet( object ){ var objectName = tools.createCSSClassName( object ); var body = object.css.style; if( object.customElement ) { var selector = this.createSelector( objectName + "Element" ); this.global_css += this.cssCache.getRule( selector, body ); var selector = this.createSelector( objectName + "Grid" ); this.global_css += this.cssCache.getRule( selector, body ); } else { var selector = this.createSelector( objectName + "Grid" ); this.global_css += this.cssCache.getRule( selector, body ); } if( object.boxRules ) { var selector = this.createSelector( objectName + "Box" ); var body = object.boxRules.style; this.global_css += this.cssCache.getRule( selector, body ); } var styleTypes = object.cssRules.styleTypes; for (var i = 0; i < styleTypes.length; i++) { var cssType = styleTypes[i]; if( object.useCustomElement ) { var selector = this.createSelector( objectName + "Element", cssType.cssSuffix ); } else { var selector = this.createSelector( objectName + "Grid", cssType.cssSuffix ); } var body = cssType.css; if( body != "" ) { //console.log("cssType.cssSuffix", cssType.cssSuffix); this.global_css += this.cssCache.getRule( selector, body ); } } if( !object.parent ) { this.writeCssToPage( this.global_css ); this.global_css = ""; } if( object.dynamic ) { this.writeCssToPage( this.global_css ); this.global_css = ""; } } writeCssToPage( css ) { if( css == "" ) { return false; } var style = document.createElement("style"); style.innerHTML = css; document.head.appendChild( style ); } removeStripe( name ) { var splitName = name.split("-"); if( splitName.length > 1 ) { return splitName[1].toLowerCase(); } else { return name; } } getStyleSheetObject(){ if( !document.body ) { return definitions.css; /* var array = JSON.parse(cssDef); var object = new Object(); for (var i = 0; i < array.length; i++) { var def = array[i]; if( this.hasUpperCase( def ) ) { object[ this.replaceUpperCase( def ) ] = ''; console.log( this.replaceUpperCase( def ) ); } else { object[ def ] = ''; } } */ return object; } if( document.body.style ) { var bodyStyle = document.body.style; bodyStyle.webkitBoxShadow = ""; return bodyStyle; } if( document.body.styleSheets ) { // fix please!!!! not allowed return document.styleSheets[1].cssRules[0].style; } else { return document.customStyleTerms; } } camelCaseToDash( name ) { for ( var i = 0; i < 4; i++ ) { if( this.hasUpperCase( name ) ) { name = this.replaceUpperCase( name ); // backgroundColor -> background-color } } return name; } removeBoxPrefix( name ) { if( !name.includes("webkit-box") ) { name = name.replace("box", "").toLowerCase(); } return name; } checkBoxSpecialCase( name ) { for (var i = 0; i < this.boxTermWithoutPrefix.length; i++) { if( this.boxTermWithoutPrefix[ i ].includes( name ) ) { return true; } } } propertyIsStyle( name ){ name = this.camelCaseToDash( name ); name = this.removeBoxPrefix( name ); if( this.checkBoxSpecialCase( name ) ) { return true; } var styleTerms = this.getStyleSheetObject(); if( typeof styleTerms[ name ] == "string" ) { return true; } else { return false; } } propertyHasStyle( name ){ var styleTerms = Object.keys( definitions.css ); if( styleTerms.includes( name.toLowerCase() ) ) { return true; } else { return false; } } }