diff --git a/CHANGELOG.md b/CHANGELOG.md index b177133..a9c7f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [1.0.12](https://github.com/CoCreate-app/CoCreate-css-parser/compare/v1.0.11...v1.0.12) (2022-11-21) + + +### Bug Fixes + +* if linkTag parse css ([8f5e368](https://github.com/CoCreate-app/CoCreate-css-parser/commit/8f5e36821c0d18fba948d2c2e4ed0f8ae6462fe8)) +* update crud functions to receive an array of objects as the response ([6edb672](https://github.com/CoCreate-app/CoCreate-css-parser/commit/6edb672978200a146b1cad8e3a697ba91fdbf0ab)) + ## [1.0.11](https://github.com/CoCreate-app/CoCreate-css-parser/compare/v1.0.10...v1.0.11) (2022-10-02) diff --git a/package.json b/package.json index ed97f99..6e77809 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cocreate/css-parser", - "version": "1.0.11", + "version": "1.0.12", "description": "A simple css-parser component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.", "keywords": [ "css-parser", diff --git a/src/index.js b/src/index.js index 01cf63a..778dffe 100644 --- a/src/index.js +++ b/src/index.js @@ -4,299 +4,304 @@ * * SPDX-License-Identifier: MIT ********************************************************************************/ - import observer from '@cocreate/observer'; - import crud from '@cocreate/crud-client'; - - const themes = ["light", "dark"]; - const mediaRangeNames = ["xs", "sm", "md", "lg", "xl"]; - const ranges = { - xs: [0, 575], - sm: [576, 768], - md: [769, 992], - lg: [993, 1200], - xl: [1201, 0], - }; - const rangesArray = Object.values(ranges); - - let classNameList = new Map(); - - let tempStyleList = []; - let parsedCSS = []; - let linkCSS = []; - let themeCSS = { dark: [], light: [] }; - let styleElSheet; - - let linkTag = document.querySelector('link[collection][document_id][name]:not([save="false"])'); - - function init() { - let parse; - if (linkTag) - parse = linkTag.getAttribute('parse'); - // if (document.querySelector('link[parse="false"]')) - // return; - - let styleEl = document.createElement("style"); - styleEl.setAttribute('component', 'CoCreateCss'); - document.head.appendChild(styleEl); - styleElSheet = styleEl.sheet; - - parseLinkCSS(); - - if (parse != false) { - let elements = document.querySelectorAll("[class]"); - initElements(elements); - } - - observerInit(); - } - - function initElements (elements) { - for(let element of elements) - initElement(element); - addNewRules(); - } - - function initElement(element) { - parseClassList(element.classList); - createThemeMedia(); - if (element.hasAttribute("className")) { - let rule = "." + element.getAttribute("className") + " { " + element.getAttribute("class").replace(/ /g, "; ").replace(/:/g, ": ") + "; }"; - tempStyleList.push(rule); - } - } - - function parseLinkCSS() { - if (linkTag && linkTag.sheet) { - let Rules = linkTag.sheet.cssRules; - if (Rules) - for (let rule of Rules) { - try { - linkCSS.push(rule.cssText); - if (rule.selectorText) { - classNameList.set(rule.selectorText.replace(/[.\\]/g, ''), true); - } else { - // ToDo: handle media queriesand add to classNameList to avoid duplicate parsing - } - } - catch (err) { - console.error(err); - } - } - } - } - - function parseClassList(classList) { - for (let className of classList) { - if (classNameList.has(className)) continue; - - if (className.includes('@dark') || className.includes('@light')) { - createThemeRule(className) ; - } - else if (className.includes(':')) { - if (className.includes('@')) { - createMediaRule(className); - } - else { - let rule = createRule(className); - tempStyleList.push(rule); - classNameList.set(className, true); - } - } - } - } - - function createRule(className) { - let important = ""; - let importantSuffix = ""; - if (className.includes('!important')) { - [className, important] = className.split("!"); - important = '!' + important; - importantSuffix = '\\' + important; - } - let res = className.split(":"); - let property = res[0]; - let suffix = parseValue(res[1]); - let value = res[1].split("@")[0].replace(/_/g, " "); - - let rule = ""; - if (res.length > 2) { - for (let i = 0; i < res.length - 2; i++) { - suffix += "\\:" + res[2 + i] + importantSuffix + ":" + res[2]; - } - rule = `.${property}\\:${suffix} { ${property}: ${value}${important}; }`; - } - else { - rule = `.${property}\\:${suffix}${importantSuffix} { ${property}: ${value}${important}; }`; - } - return rule; - } - - function parseValue(value) { - return value - .replace(/\./g, "\\.") - .replace(/%/g, "\\%") - .replace(/@/g, "\\@") - .replace(/\(/g, "\\(") - .replace(/\)/g, "\\)") - .replace(/#/g, "\\#") - .replace(/,/g, "\\,") - .replace(/!/g, "\\!") - .replace(/\//g, "\\/") - .replace(/\"/g, "\\\"") - .replace(/\'/g, "\\'"); - } - - function createMediaRule(className) { - let parts = className.split("@"); - let main_rule = createRule(className); - - for (let i = 1; i < parts.length; i++) { - let range_num = mediaRangeNames.indexOf(parts[i]); - let range = []; - if (range_num != -1) range = rangesArray[range_num]; - else { - let customRange = parts[i].split('-'); - range = customRange.map(c => Number.parseInt(c)); - } - let prefix = "@media screen"; - if (range[0] != 0) { - prefix += " and (min-width:" + range[0] + "px)"; - } - if (range[1] != 0) { - prefix += " and (max-width:" + range[1] + "px)"; - } - let rule = prefix + " { " + main_rule + "}"; - tempStyleList.push(rule); - classNameList.set(className, true); - } - } - - function createThemeRule(className) { - let classname = className; - let pseudo, theme; - [className, theme] = className.split('@'); - - if (theme.includes(':')) { - theme = theme.split(':'); - pseudo = theme; - theme = theme[0]; - pseudo.shift(); - } - - let res = className.split(':'); - if (res.length > 2) { - console.log('pseudo names need to be added after theme'); - return; - } - let property = res[0]; - let suffix = parseValue(res[1]); - let value = res[1].replace(/_/g, " "); - - let rule = ""; - if (pseudo) { - suffix += "\\@" + theme; - for (let i = 0; i < pseudo.length; i++) { - suffix += ":" + pseudo[0 + i]; - } - rule = `.${property}\\:${suffix} { ${property}: ${value}; }`; - } - else { - rule = `.${property}\\:${suffix}\\@${theme} { ${property}: ${value}; }`; - } - if (theme == 'dark' || theme == 'light') { - rule = `[theme="${theme}"] ${rule}`; - let reverseRule = `html:not([theme="${themes[1 - themes.indexOf(theme)]}"]) ${rule}`; - tempStyleList.push(rule); - themeCSS[theme].push(reverseRule); - classNameList.set(classname, true); - } - } - - function createThemeMedia() { - let initial; - if (themeCSS.dark.length) { - initial = "@media (prefers-color-scheme: dark) {"; - for (let c of themeCSS.dark) { - initial += c + "\n"; - } - initial += "}"; - tempStyleList.push(initial); - themeCSS.dark = []; - } - if (themeCSS.light.length) { - initial = "@media (prefers-color-scheme: light) {"; - for (let c of themeCSS.light) { - initial += c + "\n"; - } - initial += "}"; - tempStyleList.push(initial); - themeCSS.light = []; - } - - } - - function addNewRules() { - for (let i = 0, len = tempStyleList.length; i < len; i++) { - let rule = tempStyleList[i]; - - let low = 0, - high = parsedCSS.length; - while (low < high) { - let index = (low + high) >>> 1; - let midItem = parsedCSS[index]; - if (rule < midItem) - high = index; - else - low = index + 1; - } - - if (low > styleElSheet.cssRules.length) low = styleElSheet.cssRules.length; - try { - styleElSheet.insertRule(rule, low); - } - catch (err) { - console.error(err); - } - parsedCSS.splice(low, 0, rule); - } - if (tempStyleList.length > 0) - save(); - tempStyleList = []; - } - - function save() { - if (linkTag) { - const onlyUnique = (value, index, self) => { - return self.indexOf(value) === index; - }; - - let css = parsedCSS.concat(linkCSS).filter(onlyUnique); - crud.save(linkTag, css.join('\r\n')); - } +import observer from '@cocreate/observer'; +import CRUD from '@cocreate/crud-client'; + +let crud +if(CRUD && CRUD.default) + crud = CRUD.default +else + crud = CRUD + +const themes = ["light", "dark"]; +const mediaRangeNames = ["xs", "sm", "md", "lg", "xl"]; +const ranges = { + xs: [0, 575], + sm: [576, 768], + md: [769, 992], + lg: [993, 1200], + xl: [1201, 0], +}; +const rangesArray = Object.values(ranges); + +let classNameList = new Map(); + +let tempStyleList = []; +let parsedCSS = []; +let linkCSS = []; +let themeCSS = { dark: [], light: [] }; +let styleElSheet; + +let linkTag = document.querySelector('link[collection][document_id][name]:not([save="false"])'); + +function init() { + let parse; + if (linkTag) { + parse = linkTag.getAttribute('parse'); + + let styleEl = document.createElement("style"); + styleEl.setAttribute('component', 'CoCreateCss'); + document.head.appendChild(styleEl); + styleElSheet = styleEl.sheet; + + parseLinkCSS(); + + if (parse != false) { + let elements = document.querySelectorAll("[class]"); + initElements(elements); + } + + observerInit(); + } } - const observerInit = () => { - - observer.init({ - name: "ccCss", - observe: ['childList'], - target: '[class]', - callback: mutation => { - initElements(mutation.addedNodes); - } - }); - - observer.init({ - name: "ccCss", - observe: ["attributes"], - attributeName: ["class", "className"], - callback: mutation => { - initElements([mutation.target]); - } - }); - }; +function initElements (elements) { + for(let element of elements) + initElement(element); + addNewRules(); +} + +function initElement(element) { + parseClassList(element.classList); + createThemeMedia(); + if (element.hasAttribute("className")) { + let rule = "." + element.getAttribute("className") + " { " + element.getAttribute("class").replace(/ /g, "; ").replace(/:/g, ": ") + "; }"; + tempStyleList.push(rule); + } +} + +function parseLinkCSS() { + if (linkTag && linkTag.sheet) { + let Rules = linkTag.sheet.cssRules; + if (Rules) + for (let rule of Rules) { + try { + linkCSS.push(rule.cssText); + if (rule.selectorText) { + classNameList.set(rule.selectorText.replace(/[.\\]/g, ''), true); + } else { + // ToDo: handle media queriesand add to classNameList to avoid duplicate parsing + } + } + catch (err) { + console.error(err); + } + } + } +} + +function parseClassList(classList) { + for (let className of classList) { + if (classNameList.has(className)) continue; + + if (className.includes('@dark') || className.includes('@light')) { + createThemeRule(className) ; + } + else if (className.includes(':')) { + if (className.includes('@')) { + createMediaRule(className); + } + else { + let rule = createRule(className); + tempStyleList.push(rule); + classNameList.set(className, true); + } + } + } +} + +function createRule(className) { + let important = ""; + let importantSuffix = ""; + if (className.includes('!important')) { + [className, important] = className.split("!"); + important = '!' + important; + importantSuffix = '\\' + important; + } + let res = className.split(":"); + let property = res[0]; + let suffix = parseValue(res[1]); + let value = res[1].split("@")[0].replace(/_/g, " "); + + let rule = ""; + if (res.length > 2) { + for (let i = 0; i < res.length - 2; i++) { + suffix += "\\:" + res[2 + i] + importantSuffix + ":" + res[2]; + } + rule = `.${property}\\:${suffix} { ${property}: ${value}${important}; }`; + } + else { + rule = `.${property}\\:${suffix}${importantSuffix} { ${property}: ${value}${important}; }`; + } + return rule; +} + +function parseValue(value) { + return value + .replace(/\./g, "\\.") + .replace(/%/g, "\\%") + .replace(/@/g, "\\@") + .replace(/\(/g, "\\(") + .replace(/\)/g, "\\)") + .replace(/#/g, "\\#") + .replace(/,/g, "\\,") + .replace(/!/g, "\\!") + .replace(/\//g, "\\/") + .replace(/\"/g, "\\\"") + .replace(/\'/g, "\\'"); +} + +function createMediaRule(className) { + let parts = className.split("@"); + let main_rule = createRule(className); + + for (let i = 1; i < parts.length; i++) { + let range_num = mediaRangeNames.indexOf(parts[i]); + let range = []; + if (range_num != -1) range = rangesArray[range_num]; + else { + let customRange = parts[i].split('-'); + range = customRange.map(c => Number.parseInt(c)); + } + let prefix = "@media screen"; + if (range[0] != 0) { + prefix += " and (min-width:" + range[0] + "px)"; + } + if (range[1] != 0) { + prefix += " and (max-width:" + range[1] + "px)"; + } + let rule = prefix + " { " + main_rule + "}"; + tempStyleList.push(rule); + classNameList.set(className, true); + } +} + +function createThemeRule(className) { + let classname = className; + let pseudo, theme; + [className, theme] = className.split('@'); + + if (theme.includes(':')) { + theme = theme.split(':'); + pseudo = theme; + theme = theme[0]; + pseudo.shift(); + } + + let res = className.split(':'); + if (res.length > 2) { + console.log('pseudo names need to be added after theme'); + return; + } + let property = res[0]; + let suffix = parseValue(res[1]); + let value = res[1].replace(/_/g, " "); + + let rule = ""; + if (pseudo) { + suffix += "\\@" + theme; + for (let i = 0; i < pseudo.length; i++) { + suffix += ":" + pseudo[0 + i]; + } + rule = `.${property}\\:${suffix} { ${property}: ${value}; }`; + } + else { + rule = `.${property}\\:${suffix}\\@${theme} { ${property}: ${value}; }`; + } + if (theme == 'dark' || theme == 'light') { + rule = `[theme="${theme}"] ${rule}`; + let reverseRule = `html:not([theme="${themes[1 - themes.indexOf(theme)]}"]) ${rule}`; + tempStyleList.push(rule); + themeCSS[theme].push(reverseRule); + classNameList.set(classname, true); + } +} + +function createThemeMedia() { + let initial; + if (themeCSS.dark.length) { + initial = "@media (prefers-color-scheme: dark) {"; + for (let c of themeCSS.dark) { + initial += c + "\n"; + } + initial += "}"; + tempStyleList.push(initial); + themeCSS.dark = []; + } + if (themeCSS.light.length) { + initial = "@media (prefers-color-scheme: light) {"; + for (let c of themeCSS.light) { + initial += c + "\n"; + } + initial += "}"; + tempStyleList.push(initial); + themeCSS.light = []; + } + +} + +function addNewRules() { + for (let i = 0, len = tempStyleList.length; i < len; i++) { + let rule = tempStyleList[i]; + + let low = 0, + high = parsedCSS.length; + while (low < high) { + let index = (low + high) >>> 1; + let midItem = parsedCSS[index]; + if (rule < midItem) + high = index; + else + low = index + 1; + } + + if (low > styleElSheet.cssRules.length) low = styleElSheet.cssRules.length; + try { + styleElSheet.insertRule(rule, low); + } + catch (err) { + console.error(err); + } + parsedCSS.splice(low, 0, rule); + } + if (tempStyleList.length > 0) + save(); + tempStyleList = []; +} + +function save() { + if (linkTag) { + const onlyUnique = (value, index, self) => { + return self.indexOf(value) === index; + }; + + let css = parsedCSS.concat(linkCSS).filter(onlyUnique); + crud.save(linkTag, css.join('\r\n')); + } +} - init(); +const observerInit = () => { + + observer.init({ + name: "ccCss", + observe: ['childList'], + target: '[class]', + callback: mutation => { + initElements(mutation.addedNodes); + } + }); + + observer.init({ + name: "ccCss", + observe: ["attributes"], + attributeName: ["class", "className"], + callback: mutation => { + initElements([mutation.target]); + } + }); +}; - export default {initElements}; +init(); + +export default {initElements}; \ No newline at end of file