diff --git a/package-lock.json b/package-lock.json
index 2e2cfc8..4a0e36a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"lodash.isplainobject": "^4.0.6",
"lodash.isundefined": "^3.0.1",
"lodash.kebabcase": "^4.1.1",
+ "slate": "^0.103.0",
"uuid": "^8.3.2"
},
"devDependencies": {
@@ -2673,6 +2674,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -2762,6 +2772,14 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -4444,6 +4462,16 @@
"node": ">=8"
}
},
+ "node_modules/slate": {
+ "version": "0.103.0",
+ "resolved": "https://registry.npmjs.org/slate/-/slate-0.103.0.tgz",
+ "integrity": "sha512-eCUOVqUpADYMZ59O37QQvUdnFG+8rin0OGQAXNHvHbQeVJ67Bu0spQbcy621vtf8GQUXTEQBlk6OP9atwwob4w==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "is-plain-object": "^5.0.0",
+ "tiny-warning": "^1.0.3"
+ }
+ },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -4629,6 +4657,11 @@
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==",
"dev": true
},
+ "node_modules/tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -7017,6 +7050,11 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="
+ },
"import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -7082,6 +7120,11 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ },
"is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -8391,6 +8434,16 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
+ "slate": {
+ "version": "0.103.0",
+ "resolved": "https://registry.npmjs.org/slate/-/slate-0.103.0.tgz",
+ "integrity": "sha512-eCUOVqUpADYMZ59O37QQvUdnFG+8rin0OGQAXNHvHbQeVJ67Bu0spQbcy621vtf8GQUXTEQBlk6OP9atwwob4w==",
+ "requires": {
+ "immer": "^10.0.3",
+ "is-plain-object": "^5.0.0",
+ "tiny-warning": "^1.0.3"
+ }
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -8528,6 +8581,11 @@
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==",
"dev": true
},
+ "tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
diff --git a/package.json b/package.json
index a71ffaa..76c753f 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"lodash.isplainobject": "^4.0.6",
"lodash.isundefined": "^3.0.1",
"lodash.kebabcase": "^4.1.1",
+ "slate": "^0.103.0",
"uuid": "^8.3.2"
},
"files": [
diff --git a/src/index.tsx b/src/index.tsx
index 4a1da63..d1e48ff 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,5 +1,6 @@
import "array-flat-polyfill"
import { fromRedactor } from "./fromRedactor"
import { toRedactor } from "./toRedactor"
+import {jsonToMarkdownSerializer} from './jsonToMarkdown'
export * from "./types"
-export { fromRedactor as htmlToJson, toRedactor as jsonToHtml }
\ No newline at end of file
+export { fromRedactor as htmlToJson, toRedactor as jsonToHtml, jsonToMarkdownSerializer as jsonToMarkdown }
\ No newline at end of file
diff --git a/src/jsonToMarkdown.tsx b/src/jsonToMarkdown.tsx
new file mode 100644
index 0000000..1e0a0ba
--- /dev/null
+++ b/src/jsonToMarkdown.tsx
@@ -0,0 +1,445 @@
+import {IJsonToMarkdownElementTags, IJsonToMarkdownTextTags} from './types'
+import kebbab from 'lodash.kebabcase'
+import {Node} from 'slate'
+
+const ELEMENT_TYPES: IJsonToMarkdownElementTags = {
+ 'blockquote': (attrs: string, child: string) => {
+ return `
+
+> ${child}${attrs}`
+ },
+ 'h1': (attrs: any, child: string) => {
+ return `
+
+#${child}#`
+ },
+ 'h2': (attrs: any, child: any) => {
+ return `
+
+##${child}##`
+ },
+ 'h3': (attrs: any, child: any) => {
+ return `
+
+###${child}###`
+ },
+ 'h4': (attrs: any, child: any) => {
+ return `
+
+####${child}####`
+ },
+ 'h5': (attrs: any, child: any) => {
+ return `
+
+#####${child}#####`
+ },
+ 'h6': (attrs: any, child: any) => {
+ return `
+
+######${child}######`
+ },
+ img: (attrs: any, child: any, attrsJson: any, figureStyles: any) => {
+ if (figureStyles.fieldsEdited.length === 0) {
+ return ``
+ }
+ let img = figureStyles.anchorLink ? `` : ``
+ let caption = figureStyles.caption
+ ? figureStyles.alignment === 'center'
+ ? `${figureStyles.caption}`
+ : `${figureStyles.caption}`
+ : ''
+ let align = figureStyles.position
+ ? ``
+ : figureStyles.caption
+ ? ``
+ : `${img}`
+
+ return `${align}`
+ },
+ p: (attrs: any, child: any) => {
+ return `
+
+${child}`
+ },
+ code: (attrs: any, child: any) => {
+ return `
+
+ ${child} `
+ },
+ ol: (attrs: any, child: any) => {
+ return `${child}`
+ },
+ ul: (attrs: any, child: any) => {
+ return `${child}`
+ },
+ li: (attrs: any, child: any) => {
+ return `${child}`
+ },
+ a: (attrs: any, child: any, attrsJson: any) => {
+ return `[${child}](${attrsJson.href})`
+ },
+ hr: (attrs: any, child: any) => {
+ return `
+
+----------`
+ },
+ span: (attrs: any, child: any) => {
+ return `${child}`
+ },
+ div: (attrs: any, child: any) => {
+ return `
${child}
`
+ },
+ reference: (attrs: any, child: any, attrsJson: any, extraAttrs: any): any => {
+ if(extraAttrs?.displayType === 'display') {
+ if(attrsJson) {
+ let assetAlt = attrsJson?.alt ? attrsJson.alt : 'enter image description here'
+ let assetURL = attrsJson?.['data-sys-asset-filelink'] ? attrsJson['data-sys-asset-filelink'] : ''
+ return `
+
+![${assetAlt}]
+(${assetURL})`
+ }
+ }
+ else if(extraAttrs?.displayType === 'link') {
+ if(attrsJson) {
+ return `[${child}](${attrsJson?.['href'] ? attrsJson['href'] : "#"})`
+ }
+ }
+ },
+ fragment: (attrs: any, child: any) => {
+ return child
+ },
+}
+const TEXT_WRAPPERS: IJsonToMarkdownTextTags = {
+ 'bold': (child: any, value: any) => {
+ return `**${child}**`;
+ },
+ 'italic': (child: any, value: any) => {
+ return `*${child}*`;
+ },
+ // 'underline': (child: any, value: any) => {
+ // return `${child}`;
+ // }, underline is not supported in markdown
+ 'strikethrough': (child: any, value: any) => {
+ return `~~${child}~~`;
+ },
+ 'superscript': (child: any, value: any) => {
+ return `^${child}^`;
+ },
+ 'subscript': (child: any, value: any) => {
+ return `~${child}~`;
+ },
+ 'inlineCode': (child: any, value: any) => {
+ return `\`${child}\``
+ },
+}
+
+const getOLOrULStringFromJson = (value: any) => {
+ if(value.type === 'ol'){
+ let child = ''
+ let start = parseInt(value?.attrs?.start || 1)
+ Array.from(value.children).forEach((val: any, index) => {
+ child += `${index + start}. ${Node.string(val)}\n`
+ })
+ return `
+
+${child}`
+ }
+ if(value.type === 'ul') {
+ let child = ''
+ let symbol = value?.attrs?.listStyleType || '- '
+ Array.from(value.children).forEach((val: any, index) => {
+ child += `${symbol}${Node.string(val)}\n`
+ })
+ return `
+
+${child}`
+ }
+}
+
+export const jsonToMarkdownSerializer = (jsonValue: any): string => {
+ if (jsonValue.hasOwnProperty('text')) {
+ let text = jsonValue['text'].replace(//g, '>')
+ if (jsonValue['break']) {
+ text += `
`
+ }
+ if (jsonValue['classname'] || jsonValue['id']) {
+ if (jsonValue['classname'] && jsonValue['id']) {
+ text = `${text}`
+ }
+ else if (jsonValue['classname'] && !jsonValue['id']) {
+ text = `${text}`
+ }
+ else if (jsonValue['id'] && !jsonValue['classname']) {
+ text = `${text}`
+ }
+ }
+ if (jsonValue.text.includes('\n') && !jsonValue['break']) {
+ text = text.replace(/\n/g, '
')
+ }
+ Object.entries(jsonValue).forEach(([key, value]) => {
+ if (TEXT_WRAPPERS.hasOwnProperty(key)) {
+ text = TEXT_WRAPPERS[key](text, value)
+ }
+ })
+ if (jsonValue['attrs']) {
+ const { style } = jsonValue['attrs']
+ if (style) {
+ let attrsStyle = ''
+ if (style.color) {
+ attrsStyle = `color:${style.color};`
+ }
+ if (style["font-family"]) {
+ attrsStyle += `font-family:"${style.fontFamily}";`
+ }
+ if (style["font-size"]) {
+ attrsStyle += `font-size: ${style.fontSize};`
+ }
+ if (attrsStyle !== '') {
+ text = `${text}`
+ }
+ }
+ }
+ return text
+ }
+ let children: any = ''
+ if (jsonValue.children) {
+ children = Array.from(jsonValue.children).map((child) => jsonToMarkdownSerializer(child))
+ if (jsonValue['type'] === 'blockquote') {
+ children = children.map((child: any) => {
+ if (child === '\n') {
+ return '
'
+ }
+ return child
+ })
+ }
+ children = children.join('')
+ }
+
+ if (ELEMENT_TYPES[jsonValue['type']]) {
+ let attrs = ''
+ let attrsJson: { [key: string]: any } = {}
+ let orgType
+ let figureStyles: any = {
+ fieldsEdited: []
+ }
+ if (jsonValue.attrs) {
+
+ let allattrs = JSON.parse(JSON.stringify(jsonValue.attrs))
+ let style = ''
+ if (jsonValue.attrs["redactor-attributes"]) {
+ attrsJson = { ...allattrs["redactor-attributes"] }
+ }
+ if (jsonValue['type'] === 'reference' && jsonValue?.attrs?.default) {
+ orgType = "img"
+ let inline = ''
+ if (attrsJson['asset-link']) {
+ attrsJson['src'] = attrsJson['asset-link']
+ delete attrsJson['asset-link']
+ delete allattrs['asset-link']
+ }
+ if (attrsJson['inline']) {
+ inline = `display: flow-root;margin:0`
+ delete attrsJson['width']
+ delete attrsJson['style']
+ }
+ if (attrsJson['position']) {
+ figureStyles.position =
+ attrsJson['position'] === 'center'
+ ? `style = "margin: auto; text-align: center;width: ${allattrs['width'] ? allattrs['width'] + '%' : 100 + '%'
+ };"`
+ : `style = "float: ${attrsJson['position']};${inline};width: ${allattrs['width'] ? allattrs['width'] + '%' : 100 + '%'
+ };max-width:${allattrs['max-width'] ? allattrs['max-width'] + '%' : 100 + '%'};"`
+ figureStyles.alignment = attrsJson['position']
+ figureStyles.fieldsEdited.push(figureStyles.position)
+ delete attrsJson['position']
+ attrsJson['width'] && delete attrsJson['width']
+ attrsJson['style'] && delete attrsJson['style']
+ attrsJson['height'] && delete attrsJson['height']
+ attrsJson['max-width'] && delete attrsJson['max-width']
+ allattrs['max-width'] && delete allattrs['max-width']
+ allattrs['width'] && delete allattrs['width']
+ if (allattrs["redactor-attributes"]) {
+ allattrs["redactor-attributes"]['width'] && delete allattrs["redactor-attributes"]['width']
+ allattrs?.["redactor-attributes"]?.['style'] && delete allattrs["redactor-attributes"]['style']
+ allattrs?.["redactor-attributes"]?.['max-width'] && delete allattrs["redactor-attributes"]['max-width']
+ }
+ }
+ if (attrsJson['asset-caption']) {
+ figureStyles.caption = attrsJson['asset-caption']
+ figureStyles.fieldsEdited.push(figureStyles.caption)
+ delete attrsJson['asset-caption']
+ delete allattrs['asset-caption']
+ }
+ if (attrsJson['link']) {
+ let anchor = ''
+ anchor = `href="${attrsJson['link']}"`
+ if (attrsJson['target']) {
+ anchor += ' target="_blank"'
+ }
+ figureStyles.anchorLink = `${anchor}`
+ figureStyles.fieldsEdited.push(figureStyles.anchorLink)
+ delete attrsJson['link']
+ delete allattrs['link']
+ }
+ delete allattrs['default']
+ delete attrsJson['default']
+ delete attrsJson['target']
+ delete allattrs['asset-link']
+ delete allattrs['asset-type']
+ delete allattrs['display-type']
+
+ }
+ if (jsonValue['type'] === 'a') {
+ attrsJson['href'] = allattrs['url']
+ }
+ if (allattrs['orgType']) {
+ orgType = allattrs['orgType']
+ delete allattrs['orgType']
+ }
+ if (allattrs['class-name']) {
+ attrsJson['class'] = allattrs['class-name']
+ delete allattrs['class-name']
+ }
+ if (attrsJson['width']) {
+ let width = attrsJson['width']
+ if (width.slice(width.length - 1) === '%') {
+ style = `width: ${allattrs['width']}; height: ${attrsJson['height'] ? attrsJson['height'] : 'auto'};`
+ } else {
+ style = `width: ${allattrs['width'] + '%'}; height: ${attrsJson['height'] ? attrsJson['height'] : 'auto'};`
+ }
+ } else {
+ if (allattrs['width']) {
+ let width = String(allattrs['width'])
+
+ if (width.slice(width.length - 1) === '%') {
+ allattrs['width'] = String(allattrs['width'])
+ } else {
+ allattrs['width'] = allattrs['width'] + '%'
+ }
+ // style = `width: ${allattrs['width']}; height: auto;`
+ }
+ }
+ if (allattrs['style'] && jsonValue['type'] !== 'img') {
+ Object.keys(allattrs['style']).forEach((key) => {
+ style += `${kebbab(key)}: ${allattrs.style[key]};`
+ })
+ delete allattrs['style']
+ }
+ if (allattrs['rows'] && allattrs['cols'] && allattrs['colWidths']) {
+ delete allattrs['rows']
+ delete allattrs['cols']
+ delete allattrs['colWidths']
+ }
+ if (allattrs['disabledCols']) {
+ delete allattrs['disabledCols']
+ }
+ if (allattrs['colSpan']) {
+ delete allattrs['colSpan']
+ }
+ if (allattrs['rowSpan']) {
+ delete allattrs['rowSpan']
+ }
+
+ attrsJson = { ...attrsJson, ...allattrs, style: style }
+ if (jsonValue['type'] === 'reference') {
+ if (attrsJson['type'] === "entry") {
+ attrsJson['data-sys-entry-uid'] = allattrs['entry-uid']
+ delete attrsJson['entry-uid']
+ attrsJson['data-sys-entry-locale'] = allattrs['locale']
+ delete attrsJson['locale']
+ attrsJson['data-sys-content-type-uid'] = allattrs['content-type-uid']
+ delete attrsJson['content-type-uid']
+ attrsJson['sys-style-type'] = allattrs['display-type']
+ delete attrsJson['display-type']
+ }
+
+ else if (attrsJson['type'] === "asset") {
+ attrsJson['data-sys-asset-filelink'] = allattrs['asset-link']
+ delete attrsJson['asset-link']
+ attrsJson['data-sys-asset-uid'] = allattrs['asset-uid']
+ delete attrsJson['asset-uid']
+ attrsJson['data-sys-asset-filename'] = allattrs['asset-name']
+ delete attrsJson['asset-name']
+ attrsJson['data-sys-asset-contenttype'] = allattrs['asset-type']
+ delete attrsJson['asset-type']
+ //
+ if (allattrs['asset-caption']) {
+ attrsJson['data-sys-asset-caption'] = allattrs['asset-caption']
+ delete attrsJson['asset-caption']
+ }
+
+ if (allattrs['asset-alt']) {
+ attrsJson['data-sys-asset-alt'] = allattrs['asset-alt']
+ delete attrsJson['aasset-alt']
+ }
+
+ if (allattrs['link']) {
+ attrsJson['data-sys-asset-link'] = allattrs['link']
+ delete attrsJson['link']
+ }
+
+ if (allattrs['position']) {
+ attrsJson['data-sys-asset-position'] = allattrs['position']
+ delete attrsJson['position']
+ }
+
+ if (allattrs['target']) {
+ attrsJson['data-sys-asset-isnewtab'] = allattrs['target'] === "_blank"
+ delete attrsJson['target']
+ }
+ if (!attrsJson['sys-style-type']) {
+ attrsJson['sys-style-type'] = String(allattrs['asset-type']).indexOf('image') > -1 ? 'display' : 'download'
+ }
+ if (attrsJson?.["display-type"] === "display") {
+ const styleObj = jsonValue?.["attrs"]?.["style"] ?? {};
+ if (!styleObj["width"]) {
+ styleObj["width"] = "auto";
+ }
+ delete styleObj["float"];
+ (attrsJson["style"] && typeof attrsJson["style"] === 'string')
+ ? (attrsJson["style"] += getStyleStringFromObject(styleObj)) :
+ (attrsJson["style"] = getStyleStringFromObject(styleObj));
+ }
+ delete attrsJson['display-type']
+ }
+ }
+ if (jsonValue['type'] === "style") {
+ delete attrsJson['style-text']
+ }
+
+ delete attrsJson['redactor-attributes']
+ Object.entries(attrsJson).forEach((key) => {
+ return key[1] ? (key[1] !== '' ? (attrs += `${key[0]}="${key[1]}" `) : '') : ''
+ })
+ attrs = (attrs.trim() ? ' ' : '') + attrs.trim()
+ }
+
+ if(jsonValue['type'] === 'ol' || jsonValue['type'] === 'ul') {
+ //@ts-ignore
+ return getOLOrULStringFromJson(jsonValue)
+ }
+
+ if (jsonValue['type'] === 'reference') {
+ figureStyles.displayType = jsonValue?.attrs?.["display-type"]
+ }
+
+ if (jsonValue['type'] === 'span' && jsonValue.children.length === 1 && jsonValue.children[0].type === 'span') {
+ if (Object.keys(jsonValue.attrs).length === 0) {
+ return children
+ }
+ }
+
+ attrs = (attrs.trim() ? ' ' : '') + attrs.trim()
+
+ return ELEMENT_TYPES[orgType || jsonValue['type']](attrs, children, attrsJson, figureStyles)
+ }
+ return children
+}
+
+
+function getStyleStringFromObject(styleObj: { [key: string]: string }) {
+ return Object.keys(styleObj)
+ .map((key) => `${key}: ${styleObj[key]}`)
+ .join("; ");
+}
diff --git a/src/types.ts b/src/types.ts
index f5c79a3..5c086a6 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -14,6 +14,8 @@ export interface IHtmlToJsonElementTags { [key: string]: (el:HTMLElement) => IHt
export interface IJsonToHtmlTextTags { [key: string]: (child:any, value:any) => string }
export interface IJsonToHtmlElementTags { [key: string]: (attrs:string,child:string,jsonBlock:IAnyObject,extraProps?:object) => string }
+export interface IJsonToMarkdownElementTags{[key: string]: (attrs:string,child:string,attrsJson:IAnyObject,extraProps?:object) => string}
+export interface IJsonToMarkdownTextTags{ [key: string]: (child:any, value:any) => string }
export interface IJsonToHtmlOptions {
customElementTypes?: IJsonToHtmlElementTags,
customTextWrapper?: IJsonToHtmlTextTags,