From 04c644cec48a4dfad117d04c6fa4ee7d20cecda4 Mon Sep 17 00:00:00 2001 From: eliot-akira Date: Sun, 29 Sep 2024 16:59:10 +0200 Subject: [PATCH] Move Fields Example pages and assets --- assets/build/example.min.css | 2 + assets/build/example.min.js | 1 + assets/src/example/index.js | 39 + assets/src/example/index.scss | 84 ++ assets/src/example/register-custom-type.jsx | 66 + assets/vendor/github-dark-dimmed.css | 117 ++ assets/vendor/highlight.min.js | 1213 +++++++++++++++++ example/Documentation.php | 76 ++ example/ajax/index.php | 67 + example/enqueue.php | 46 + example/index.php | 102 ++ example/register/fields.php | 49 + example/register/index.php | 6 + example/register/sections.php | 468 +++++++ .../templates/conditional-logic/create.php | 72 + .../templates/conditional-logic/evaluate.md | 1 + example/templates/context/usage.md | 66 + .../dependent-attributes/example.php | 121 ++ .../templates/dependent-attributes/syntax.php | 41 + example/templates/dynamic-values/examples.php | 119 ++ example/templates/dynamic-values/list.php | 45 + .../templates/dynamic-values/registration.md | 126 ++ example/templates/dynamic-values/usage.md | 102 ++ example/templates/elements/button.php | 109 ++ example/templates/elements/description.php | 41 + example/templates/elements/label.php | 41 + example/templates/elements/modal.php | 55 + example/templates/elements/tooltip.php | 152 +++ example/templates/fields/accordion.php | 92 ++ example/templates/fields/alignment-matrix.php | 36 + example/templates/fields/angle-picker.php | 41 + example/templates/fields/border.php | 83 ++ example/templates/fields/button-group.php | 82 ++ example/templates/fields/checkbox.php | 37 + example/templates/fields/code.php | 38 + example/templates/fields/color-picker.php | 69 + example/templates/fields/combo-box-async.php | 177 +++ example/templates/fields/combo-box.php | 133 ++ example/templates/fields/date-picker.php | 114 ++ example/templates/fields/dimensions.php | 75 + example/templates/fields/field-group.php | 61 + example/templates/fields/file.php | 136 ++ example/templates/fields/gallery.php | 38 + example/templates/fields/gradient.php | 38 + example/templates/fields/hidden.php | 48 + example/templates/fields/list.php | 80 ++ example/templates/fields/number.php | 56 + example/templates/fields/radio.php | 45 + example/templates/fields/select.php | 160 +++ example/templates/fields/select3.php | 70 + example/templates/fields/simple-dimension.php | 38 + example/templates/fields/switch.php | 40 + example/templates/fields/text-suggestion.php | 97 ++ example/templates/fields/text.php | 146 ++ example/templates/fields/textarea.php | 57 + example/templates/fields/wysiwyg.php | 83 ++ example/templates/installation/composer.md | 37 + .../templates/introduction/introduction.md | 46 + example/templates/javascript/events.md | 92 ++ example/templates/javascript/fields.md | 6 + example/templates/javascript/render.md | 92 ++ example/templates/javascript/store.md | 89 ++ example/templates/javascript/types.php | 106 ++ example/templates/registration/save-load.md | 222 +++ example/templates/registration/usage.md | 24 + example/templates/repeater/advanced.php | 86 ++ example/templates/repeater/bare.php | 81 ++ example/templates/repeater/block.php | 251 ++++ example/templates/repeater/table.php | 197 +++ example/templates/visibility/example.php | 116 ++ example/templates/visibility/operators.php | 95 ++ example/templates/visibility/syntax.php | 30 + tangible.config.js | 16 +- 73 files changed, 7181 insertions(+), 2 deletions(-) create mode 100644 assets/build/example.min.css create mode 100644 assets/build/example.min.js create mode 100644 assets/src/example/index.js create mode 100644 assets/src/example/index.scss create mode 100644 assets/src/example/register-custom-type.jsx create mode 100644 assets/vendor/github-dark-dimmed.css create mode 100644 assets/vendor/highlight.min.js create mode 100644 example/Documentation.php create mode 100644 example/ajax/index.php create mode 100644 example/enqueue.php create mode 100644 example/index.php create mode 100644 example/register/fields.php create mode 100644 example/register/index.php create mode 100644 example/register/sections.php create mode 100644 example/templates/conditional-logic/create.php create mode 100644 example/templates/conditional-logic/evaluate.md create mode 100644 example/templates/context/usage.md create mode 100644 example/templates/dependent-attributes/example.php create mode 100644 example/templates/dependent-attributes/syntax.php create mode 100644 example/templates/dynamic-values/examples.php create mode 100644 example/templates/dynamic-values/list.php create mode 100644 example/templates/dynamic-values/registration.md create mode 100644 example/templates/dynamic-values/usage.md create mode 100644 example/templates/elements/button.php create mode 100644 example/templates/elements/description.php create mode 100644 example/templates/elements/label.php create mode 100644 example/templates/elements/modal.php create mode 100644 example/templates/elements/tooltip.php create mode 100644 example/templates/fields/accordion.php create mode 100644 example/templates/fields/alignment-matrix.php create mode 100644 example/templates/fields/angle-picker.php create mode 100644 example/templates/fields/border.php create mode 100644 example/templates/fields/button-group.php create mode 100644 example/templates/fields/checkbox.php create mode 100644 example/templates/fields/code.php create mode 100644 example/templates/fields/color-picker.php create mode 100644 example/templates/fields/combo-box-async.php create mode 100644 example/templates/fields/combo-box.php create mode 100644 example/templates/fields/date-picker.php create mode 100644 example/templates/fields/dimensions.php create mode 100644 example/templates/fields/field-group.php create mode 100644 example/templates/fields/file.php create mode 100644 example/templates/fields/gallery.php create mode 100644 example/templates/fields/gradient.php create mode 100644 example/templates/fields/hidden.php create mode 100644 example/templates/fields/list.php create mode 100644 example/templates/fields/number.php create mode 100644 example/templates/fields/radio.php create mode 100644 example/templates/fields/select.php create mode 100644 example/templates/fields/select3.php create mode 100644 example/templates/fields/simple-dimension.php create mode 100644 example/templates/fields/switch.php create mode 100644 example/templates/fields/text-suggestion.php create mode 100644 example/templates/fields/text.php create mode 100644 example/templates/fields/textarea.php create mode 100644 example/templates/fields/wysiwyg.php create mode 100644 example/templates/installation/composer.md create mode 100644 example/templates/introduction/introduction.md create mode 100644 example/templates/javascript/events.md create mode 100644 example/templates/javascript/fields.md create mode 100644 example/templates/javascript/render.md create mode 100644 example/templates/javascript/store.md create mode 100644 example/templates/javascript/types.php create mode 100644 example/templates/registration/save-load.md create mode 100644 example/templates/registration/usage.md create mode 100644 example/templates/repeater/advanced.php create mode 100644 example/templates/repeater/bare.php create mode 100644 example/templates/repeater/block.php create mode 100644 example/templates/repeater/table.php create mode 100644 example/templates/visibility/example.php create mode 100644 example/templates/visibility/operators.php create mode 100644 example/templates/visibility/syntax.php diff --git a/assets/build/example.min.css b/assets/build/example.min.css new file mode 100644 index 00000000..c2bbadf7 --- /dev/null +++ b/assets/build/example.min.css @@ -0,0 +1,2 @@ +.tangible-field-example-settings pre{background:#22272e}.tangible-field-example-settings .tangible-plugin-settings-tab{max-width:100%}.tangible-field-example-settings .tangible-settings-row{padding:10px 0}.tangible-field-example-settings .tf-example-container{display:flex}.tangible-field-example-settings .tf-dynamic-value-content-js ul,.tangible-field-example-settings .tf-example-field>ul{list-style:circle;padding:10px}.tangible-field-example-settings .tf-dynamic-value-content-js ul ul,.tangible-field-example-settings .tf-example-field>ul ul{list-style:square;padding:5px 5px 5px 40px}.tangible-field-example-settings .tf-example-list{padding-top:20px;width:20%}.tangible-field-example-settings .tf-example-field{width:80%}.tangible-field-example-settings .tf-context-select-container{padding:20px 0}.tangible-field-example-settings .tf-dynamic-value-content-js{display:flex;flex-direction:column;gap:10px}.tangible-field-example-settings .tf-dynamic-value-description{padding-bottom:10px}.tangible-field-example-settings .tf-dynamic-value-list{display:flex}.tangible-field-example-settings .tf-dynamic-value-list-sidebar{display:flex;flex-direction:column;width:33%}.tangible-field-example-settings .tf-dynamic-value-list-content{display:flex;flex-direction:column}.tangible-field-example-settings .tf-dynamic-value-trigger-js{cursor:pointer}.tangible-field-example-settings .tangible-settings-code-preview{display:flex;flex-wrap:nowrap;gap:20px}.tangible-field-example-settings .tangible-settings-code-preview>pre{flex:1} +/*# sourceMappingURL=example.min.css.map */ \ No newline at end of file diff --git a/assets/build/example.min.js b/assets/build/example.min.js new file mode 100644 index 00000000..b4c9c1cc --- /dev/null +++ b/assets/build/example.min.js @@ -0,0 +1 @@ +var u=Object.defineProperty;var m=(i,s)=>u(i,"name",{value:s,configurable:!0});(function(){"use strict";(function(){const t={NODE_ENV:"production"};try{if(process){process.env=Object.assign({},process.env),Object.assign(process.env,t);return}}catch{}globalThis.process={env:t}})();var i=Object.defineProperty,s=m((t,a)=>i(t,"name",{value:a,configurable:!0}),"s");const d=s(()=>{const t=document.getElementById("tf-example-custom-types"),a=t.getAttribute("data-value"),o=s(e=>{const{initJSON:r}=tangibleFields.utils,[n,c]=wp.element.useState(r(e.value??{}));return wp.element.useEffect(()=>{e.onChange&&e.onChange(n)},[n]),wp.element.createElement("div",{className:"tf-custom-type",style:{display:"flex"}},wp.element.createElement("input",{type:"hidden",name:e.name??"",value:JSON.stringify(n)}),wp.element.createElement("div",null,wp.element.createElement("strong",null,"Character attributes:"),wp.element.createElement("hr",null),tangibleFields.render({label:"First name",type:"text",value:n.firstName??"",onChange:l=>c({...n,firstName:l})}),tangibleFields.render({label:"Last name",type:"text",value:n.lastName,onChange:l=>c({...n,lastName:l})}),wp.element.createElement("hr",null),tangibleFields.render({label:"Avatar",type:"file",maxUpload:1,wp_media:!1,value:n.avatar??"",onChange:l=>c({...n,avatar:l}),mimeTypes:["image"]})))},"MyCustomField");tangibleFields.types.add("custom-type",o),wp.element.createRoot(t).render(tangibleFields.render({type:"custom-type",name:"custom-field-example",value:a}))},"initExample");window.addEventListener("load",()=>{const t=document.getElementById("tf-context-select");t.addEventListener("change",()=>{const e=new URL(location.href),r=new URLSearchParams(e.search);r.set("context",t.value),location.replace(location.protocol+"//"+location.host+location.pathname+"?"+r.toString())}),document.getElementById("tf-example-custom-types")&&d();const a=document.getElementsByClassName("tf-dynamic-value-trigger-js"),o=document.getElementsByClassName("tf-dynamic-value-content-js");if(a.length!==0)for(let e=0;e{jQuery(".tf-dynamic-value-content-js").attr("style","display: none"),o[e].setAttribute("style","")})})})(); diff --git a/assets/src/example/index.js b/assets/src/example/index.js new file mode 100644 index 00000000..e5380d74 --- /dev/null +++ b/assets/src/example/index.js @@ -0,0 +1,39 @@ +import { initExample } from './register-custom-type' + +/** + * Settings page: Change context on select change + */ +window.addEventListener('load', () => { + + const select = document.getElementById('tf-context-select') + + select.addEventListener('change', () => { + + const url = new URL(location.href); + const params = new URLSearchParams(url.search) + + params.set('context', select.value) + + location.replace(location.protocol + '//' + location.host + location.pathname + '?' + params.toString()) + }) + + /** + * Example for custom field type registration + */ + if( document.getElementById('tf-example-custom-types') ) initExample() + + /** + * Specific to dynamic value list page - Handle list selection + */ + const trigger = document.getElementsByClassName('tf-dynamic-value-trigger-js') + const content = document.getElementsByClassName('tf-dynamic-value-content-js') + + if( trigger.length === 0 ) return; + + for (let i = 0; i < trigger.length; i++) { + trigger[i].addEventListener('click', () => { + jQuery('.tf-dynamic-value-content-js').attr('style', 'display: none') + content[i].setAttribute('style', '') + }) + } +}) diff --git a/assets/src/example/index.scss b/assets/src/example/index.scss new file mode 100644 index 00000000..addada9b --- /dev/null +++ b/assets/src/example/index.scss @@ -0,0 +1,84 @@ +.tangible-field-example-settings { + + pre { + background: #22272e; + } + + .tangible-plugin-settings-tab { + max-width: 100%; + } + + .tangible-settings-row { + padding: 10px 0px; + } + + .tf-example-container { + display: flex; + } + + .tf-example-field > ul, + .tf-dynamic-value-content-js ul { + list-style: circle; + padding: 10px; + + ul { + list-style: square; + padding: 5px; + padding-left: 40px; + } + } + + .tf-example-list { + width: 20%; + padding-top: 20px; + } + + .tf-example-field { + width: 80%; + } + + .tf-context-select-container { + padding: 20px 0px; + } + + .tf-dynamic-value-content-js { + display: flex; + flex-direction: column; + gap: 10px; + } + + .tf-dynamic-value-description { + padding-bottom: 10px; + } + + .tf-dynamic-value-list { + display: flex; + } + + .tf-dynamic-value-list-sidebar { + width: 33%; + display: flex; + flex-direction: column; + } + + .tf-dynamic-value-list-content { + display: flex; + flex-direction: column; + } + + .tf-dynamic-value-trigger-js { + cursor: pointer; + } + + .tangible-settings-code-preview { + display: flex; + gap: 20px; + flex-wrap: nowrap; + + & > pre { + flex: 1; + } + } +} + + diff --git a/assets/src/example/register-custom-type.jsx b/assets/src/example/register-custom-type.jsx new file mode 100644 index 00000000..289d4496 --- /dev/null +++ b/assets/src/example/register-custom-type.jsx @@ -0,0 +1,66 @@ +import { + useState, + useEffect, + createRoot +} from 'react' + +const initExample = () => { + + const container = document.getElementById('tf-example-custom-types') + const value = container.getAttribute('data-value') + + const MyCustomField = props => { + + const { initJSON } = tangibleFields.utils + const [value, setValue] = useState( initJSON(props.value ?? {}) ) + + useEffect(() => { + if( props.onChange ) props.onChange(value) + }, [value]) + + return( +
+ +
+ Character attributes: +
+ { tangibleFields.render({ + label : 'First name', + type :'text', + value : value.firstName ?? '', + onChange : firstName => setValue({ ...value, firstName: firstName }), + }) } + { tangibleFields.render({ + label : 'Last name', + type :'text', + value : value.lastName, + onChange : lastName => setValue({ ...value, lastName: lastName }), + }) } +
+ { tangibleFields.render({ + label : 'Avatar', + type : 'file', + maxUpload : 1, + wp_media : false, + value : value.avatar ?? '', + onChange : avatar => setValue({ ...value, avatar: avatar }), + mimeTypes : [ 'image' ] + }) } +
+
+ ) + } + + tangibleFields.types.add('custom-type', MyCustomField) + + createRoot(container) + .render( + tangibleFields.render({ + 'type' :'custom-type', + 'name' :'custom-field-example', + 'value' : value + } + )) +} + +export { initExample } diff --git a/assets/vendor/github-dark-dimmed.css b/assets/vendor/github-dark-dimmed.css new file mode 100644 index 00000000..f3855ca1 --- /dev/null +++ b/assets/vendor/github-dark-dimmed.css @@ -0,0 +1,117 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +/*! + Theme: GitHub Dark Dimmed + Description: Dark dimmed theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Colors taken from GitHub's CSS +*/ +.hljs { + color: #adbac7; + background: #22272e +} +.hljs-doctag, +.hljs-keyword, +.hljs-meta .hljs-keyword, +.hljs-template-tag, +.hljs-template-variable, +.hljs-type, +.hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #f47067 +} +.hljs-title, +.hljs-title.class_, +.hljs-title.class_.inherited__, +.hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #dcbdfb +} +.hljs-attr, +.hljs-attribute, +.hljs-literal, +.hljs-meta, +.hljs-number, +.hljs-operator, +.hljs-variable, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-id { + /* prettylights-syntax-constant */ + color: #6cb6ff +} +.hljs-regexp, +.hljs-string, +.hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #96d0ff +} +.hljs-built_in, +.hljs-symbol { + /* prettylights-syntax-variable */ + color: #f69d50 +} +.hljs-comment, +.hljs-code, +.hljs-formula { + /* prettylights-syntax-comment */ + color: #768390 +} +.hljs-name, +.hljs-quote, +.hljs-selector-tag, +.hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #8ddb8c +} +.hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #adbac7 +} +.hljs-section { + /* prettylights-syntax-markup-heading */ + color: #316dca; + font-weight: bold +} +.hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #eac55f +} +.hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #adbac7; + font-style: italic +} +.hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #adbac7; + font-weight: bold +} +.hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #b4f1b4; + background-color: #1b4721 +} +.hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #ffd8d3; + background-color: #78191b +} +.hljs-char.escape_, +.hljs-link, +.hljs-params, +.hljs-property, +.hljs-punctuation, +.hljs-tag { + /* purposely ignored */ + +} \ No newline at end of file diff --git a/assets/vendor/highlight.min.js b/assets/vendor/highlight.min.js new file mode 100644 index 00000000..5d699ae6 --- /dev/null +++ b/assets/vendor/highlight.min.js @@ -0,0 +1,1213 @@ +/*! + Highlight.js v11.9.0 (git: f47103d4f1) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(n){ +return n instanceof Map?n.clear=n.delete=n.set=()=>{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ +begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, +C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" +;function U(e,n,t=F){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ +console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) +},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), +G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), +G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ +return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) +;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" +;for(;n;){t+=A.substring(e,n.index) +;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ +const[e,a]=r +;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a +;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ +if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ +if(!a[x.subLanguage])return void S.addText(A) +;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top +}else e=E(A,x.subLanguage.length?x.subLanguage:null) +;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) +})():c(),A=""}function g(e,n){ +""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ +value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x +;x.endScope&&x.endScope._wrap?(d(), +g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), +u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), +d(),r.excludeEnd&&(A=n));do{ +x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent +}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?A+=t:(a.excludeBegin&&(A+=t), +d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') +;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} +if("illegal"===r.type&&""===o)return 1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return A+=o,o.length}const w=v(e) +;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] +;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ +if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ +R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T +;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) +;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, +value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ +language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 +;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) +;return n||(H(o.replace("{}",t[1])), +H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return +;if(x("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 +}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function k(e){const n=v(e) +;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(K("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;K(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ +scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) +;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ +className:"number",variants:[{ +begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ +begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ +begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} +const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) +;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,k,{match:/\$[(.]/}]}} +const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, +grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], +case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ +className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ +match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ +begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 +}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, +contains:g}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ +scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ +keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ +match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},E={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, +begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] +},N={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) +;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, +keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, +contains:["self",...w]},...w,{scope:"meta",match:i}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, +contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ +1:"keyword",3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, +grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+ce.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ +begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:l.concat(s),literal:a,type:i}},{className:"type", +begin:n.either("double precision","large object","with timezone","without timezone") +},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 +},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ +match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), +relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ +match:b(/\./,m(...De)),relevance:0},{className:"built_in", +match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] +}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ +className:"string", +variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] +},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) +},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ +scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ +match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") +},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 +},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ +1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} +;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, +contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] +},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 +},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ +const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, +contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, +keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", +begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;return Object.assign(n.keywords,s), +n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), +l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, +end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", +contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ +const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} +return He}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/example/Documentation.php b/example/Documentation.php new file mode 100644 index 00000000..7ed4045d --- /dev/null +++ b/example/Documentation.php @@ -0,0 +1,76 @@ +sections[ $slug ] = $args; + } + + function register_page_fields(string $section, string $page) { + + if( ! $page = ($this->sections[$section]['pages'][$page] ?? false) ) { + return; + } + + foreach($page['fields'] ?? [] as $field => $args) { + self::$plugin->register_options($field, $args); + $this->maybe_save_fields($field); + } + } + + function maybe_save_fields(string $field) { + + if( ! isset($_POST[$field]) ) return; + + Documentation::$fields->store_value($field, $_POST[$field]); + } + + function require_template(string $section, string $page) { + + if( ! $section = ($this->sections[$section] ?? false) ) return; + if( ! $page = ($section['pages'][$page] ?? false) ) return; + + ?>

-

text($content); + } + else { + require_once $path . $extension; + } + } + + function start_code($language = 'php') { + ?>
get_results( + $wpdb->prepare( + "SELECT * FROM {$wpdb->posts} + WHERE (`post_title` LIKE %s) + AND `post_type` IN ({$plugin->prepare_sql_in_statement($post_type)}) + ORDER BY `post_title` {$order} + LIMIT %d", + '%' . $wpdb->esc_like($post_title) . '%', + $limit + ) + ); + + $response = array_map(function($post) { + return [ 'id' => $post->ID, 'title' => $post->post_title ]; + }, $results); + + return $response; +}); + +$plugin->prepare_sql_in_statement = function($array) { + + global $wpdb; + + $escaped = []; + foreach($array as $value) { + $escaped[] = $wpdb->prepare('%s', $value); + } + + return implode(',', $escaped); +}; diff --git a/example/enqueue.php b/example/enqueue.php new file mode 100644 index 00000000..be23e860 --- /dev/null +++ b/example/enqueue.php @@ -0,0 +1,46 @@ +enqueue = function() use($plugin, $fields) { + + add_action('admin_footer', function() use($fields) { + $fields->enqueue([ + 'context' => [ + 'default', + 'wp', + 'elementor', + 'beaver-builder' + ] + ]); + }); + + wp_enqueue_script( + 'fields-example', + $plugin->assets_url . '/build/example.min.js', + ); + wp_enqueue_style( + 'fields-example', + $plugin->assets_url . '/build/example.min.css', + ); + + wp_enqueue_script( + 'highlight', + // 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js', + $plugin->assets_url . '/vendor/highlight.min.js', + [], + '11.9.0' + ); + + wp_enqueue_style( + 'highlight-theme', + // 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark-dimmed.css', + $plugin->assets_url . '/vendor/github-dark-dimmed.css', + [], + '11.9.0' + ); + + +}; diff --git a/example/index.php b/example/index.php new file mode 100644 index 00000000..b461e5e3 --- /dev/null +++ b/example/index.php @@ -0,0 +1,102 @@ + 'tangible-field-example', + 'title' => 'Tangible Field Example', + 'setting_prefix' => 'tangible_field_example', + + 'version' => tangible_fields()->version, + 'file_path' => __FILE__, + 'base_path' => plugin_basename( __FILE__ ), + 'dir_path' => plugin_dir_path( __FILE__ ), + 'url' => plugins_url( '/', __FILE__ ), + 'assets_url' => plugins_url( '/../assets', __FILE__ ), +]); + +if (!function_exists('tangible_field_example')) { + function tangible_field_example($instance = false) { + static $plugin; + return $plugin ? $plugin : ($plugin = $instance); + } +} + +tangible_field_example($plugin); + +require_once __DIR__ . '/ajax/index.php'; +require_once __DIR__ . '/Documentation.php'; +require_once __DIR__ . '/register/index.php'; +require_once __DIR__ . '/enqueue.php'; + +framework\register_admin_menu([ + 'name' => 'tangible-field-example', + 'title' => 'Fields Example', + // 'css' => $plugin->assets_url . '/build/settings.min.css', + // 'js' => $plugin->assets_url . '/build/settings.min.js', + 'callback' => function() use ($fields, $plugin) { + $documentation = Documentation::$instance; + $plugin->enqueue(); + + $current_page = $_GET['type'] ?? 'introduction'; + $current_section = $_GET['section'] ?? 'introduction'; + + if( $current_page && $current_section ) { + $documentation->register_page_fields($current_section, $current_page); + } + + $current_context = $_GET['context'] ?? 'default'; + $current_page_url = admin_url( sprintf('admin.php?%s', http_build_query($_GET)) ); + + $fields->set_context($current_context); + + ?> + +
+
+ + +
+ + + Context +
+ +
+ + + sections as $section_slug => $section): ?> + +
    + $page): ?> +
  • + + + +
  • + +
+ +
+ + + +
+ require_template($current_section, $current_page) ?> +
+ + +
+
register_options = function(string $name, array $args = []) use($fields) { + $fields->register_field($name, + $args + + ( + empty($args['json']) + ? $fields->_store_callbacks['options']('tfe_') + : ([ + 'store_callback' => $fields->_store_callbacks['options']('tfe_')['store_callback'], + 'fetch_callback' => function($name) use($fields) { + $value =$fields->_store_callbacks['options']('tfe_')['fetch_callback']($name); + return stripslashes($value); // We need to stripslashes to return valid JSON + } + ]) + ) + + $fields->_permission_callbacks([ + 'store' => ['user_can', 'manage_options'], + 'fetch' => ['always_allow'] + ]) + ); +}; + +/** + * We don't want to assume that visitors know that it's required to register a field, so we excplicitly add + * the registration in all of our examples + * + * This function is just an helper so that we don't have to copy/paste the registration sample everywhere + */ +$plugin->render_registation_message = function() { +?> + + /** + * Your field must be registered, otherwise you won't be able to use fetch/store_value + * See the "Registration" section for more details + * + * @see https://bitbucket.org/tangibleinc/tangible-fields-module/src/main/store.php + */ + register_section('introduction', [ + 'title' => 'Introduction', + 'pages' => [ + 'introduction' => [ + 'title' => 'Field module', + 'path' => 'introduction/introduction' + ] + ] +]); + +$documentation->register_section('installation', [ + 'title' => 'Installation', + 'pages' => [ + 'composer' => [ + 'title' => 'Composer', + 'path' => 'installation/composer' + ] + ] +]); + +$documentation->register_section('registration', [ + 'title' => 'Registration', + 'pages' => [ + 'registration-usage' => [ + 'title' => 'Usage', + 'path' => 'registration/usage' + ], + 'registration-save-load' => [ + 'title' => 'Save and load', + 'path' => 'registration/save-load' + ], + ] +]); + +$documentation->register_section('fields', [ + 'title' => 'Field types', + 'pages' => [ + 'accordion' => [ + 'title' => 'Accordion', + 'path' => 'fields/accordion', + 'fields'=> [ + 'accordion' => [ 'json' => true ], + 'accordion-with-switch' => [ 'json' => true ] + ] + ], + 'alignment_matrix' => [ + 'title' => 'Alignment matrix', + 'path' => 'fields/alignment-matrix', + 'fields'=> [ + 'alignment_matrix' => [] + ] + ], + 'angle_picker' => [ + 'title' => 'Angle picker', + 'path' => 'fields/angle-picker', + 'fields'=> [ + 'angle_picker' => [], + ] + ], + 'border' => [ + 'title' => 'Border', + 'path' => 'fields/border', + 'fields'=> [ + 'border' => [ 'json' => true ], + 'border_unlinked' => [ 'json' => true ], + 'border_linked' => [ 'json' => true ] + ] + ], + 'button_group' => [ + 'title' => 'Button group', + 'path' => 'fields/button-group', + 'fields'=> [ + 'button_group_dashicon' => [], + 'button_group' => [] + ] + ], + 'checkbox' => [ + 'title' => 'Checkbox', + 'path' => 'fields/checkbox', + 'fields'=> [ + 'checkbox' => [] + ] + ], + 'code' => [ + 'title' => 'Code', + 'path' => 'fields/code', + 'fields'=> [ + 'code' => [], + ] + ], + 'color_picker' => [ + 'title' => 'Color picker', + 'path' => 'fields/color-picker', + 'fields'=> [ + 'color' => [], + 'color_opacity' => [] + ] + ], + 'combo_box' => [ + 'title' => 'ComboBox', + 'path' => 'fields/combo-box', + 'fields'=> [ + 'combobox' => [], + 'combobox_multiple' => [], + 'combobox_categories' => [] + ] + ], + 'combo_box_async' => [ + 'title' => 'ComboBox - Async mode', + 'path' => 'fields/combo-box-async', + 'fields'=> [ + 'combobox_async_rest' => [ 'json' => true ], + 'combobox_async_ajax' => [ 'json' => true ], + 'combobox_async_multiple' => [ 'json' => true ] + ] + ], + 'date_picker' => [ + 'title' => 'Date picker', + 'path' => 'fields/date-picker', + 'fields'=> [ + 'date' => [], + 'date_future_only' => [], + 'date_range' => [ 'json' => true ], + 'multi_month' => [ 'json' => true ], + 'date_presets' => [ 'json' => true ], + ] + ], + 'dimensions' => [ + 'title' => 'Dimensions', + 'path' => 'fields/dimensions', + 'fields'=> [ + 'dimensions' => [ 'json' => true ], + 'dimensions_linked' => [ 'json' => true ], + 'dimensions_not_linked' => [ 'json' => true ] + ] + ], + 'field_group' => [ + 'title' => 'Field group', + 'path' => 'fields/field-group', + 'fields'=> [ + 'field_group' => [ 'json' => true ], + ] + ], + 'file' => [ + 'title' => 'File', + 'path' => 'fields/file', + 'fields'=> [ + 'file' => [], + 'file_input' => [], + 'file_wp_media' => [], + 'file_image' => [], + 'file_limited' => [] + ] + ], + 'gallery' => [ + 'title' => 'Gallery', + 'path' => 'fields/gallery', + 'fields'=> [ + 'gallery' => [] + ] + ], + 'gradient' => [ + 'title' => 'Gradient', + 'path' => 'fields/gradient', + 'fields'=> [ + 'gradient' => [ 'json' => true ] + ] + ], + 'hidden' => [ + 'title' => 'Hidden input', + 'path' => 'fields/hidden', + 'fields'=> [ + 'hidden' => [] + ] + ], + 'list' => [ + 'title' => 'List', + 'path' => 'fields/list', + 'fields'=> [ + 'list' => [ 'json' => true ], + 'list-with-visibility' => [ 'json' => true ] + ] + ], + 'number' => [ + 'title' => 'Number', + 'path' => 'fields/number', + 'fields'=> [ + 'number' => [], + 'number_max' => [] + ] + ], + 'radio' => [ + 'title' => 'Radio', + 'path' => 'fields/radio', + 'fields'=> [ + 'radio' => [] + ] + ], + 'select' => [ + 'title' => 'Select', + 'path' => 'fields/select', + 'fields'=> [ + 'select' => [], + 'select_categories' => [], + 'select_multiple' => [], + 'select_multiple_categories' => [], + ] + ], +// 'select3' => [ +// 'title' => 'Select3', +// 'path' => 'fields/select3', +// 'fields'=> [ +// 'select3_single_select' => [], +// 'select3_multi_select' => [], +// ] +// ], + 'simple_dimension' => [ + 'title' => 'Simple dimension', + 'path' => 'fields/simple-dimension', + 'fields'=> [ + 'simple_dimension' => [ 'json' => true ] + ] + ], + 'switch' => [ + 'title' => 'Switch', + 'path' => 'fields/switch', + 'fields'=> [ + 'switch' => [] + ] + ], + 'text' => [ + 'title' => 'Text', + 'path' => 'fields/text', + 'fields'=> [ + 'text' => [], + 'text-read-only' => [], + 'text-mask' => [], + 'text-prefix-suffix' => [], + 'text-prefix-suffix-mask' => [], + ] + ], + 'text_suggestion' => [ + 'title' => 'Text suggestion', + 'path' => 'fields/text-suggestion', + 'fields'=> [ + 'dynamic_text' => [], + 'dynamic_text_categories' => [] + ] + ], + 'textarea' => [ + 'title' => 'Textarea', + 'path' => 'fields/textarea', + 'fields'=> [ + 'textarea' => [] + ] + ], + 'wysiwyg' => [ + 'title' => 'WYSIWYG', + 'path' => 'fields/wysiwyg', + 'fields'=> [ + 'wysiwyg' => [], + 'wysiwyg-visual' => [], + 'wysiwyg-tinymce' => [] + ] + ] + ] +]); + +$documentation->register_section('repeater', [ + 'title' => 'Repeater', + 'pages' => [ + 'advanced' => [ + 'title' => 'Advanced', + 'path' => 'repeater/advanced', + 'fields'=> [ + 'repeater_advanced' => [ 'json' => true ], + ] + ], + 'bare' => [ + 'title' => 'Bare', + 'path' => 'repeater/bare', + 'fields'=> [ + 'repeater_bare' => [ 'json' => true ], + ] + ], + 'block' => [ + 'title' => 'Block', + 'path' => 'repeater/block', + 'fields'=> [ + 'repeater_block' => [ 'json' => true ], + 'repeater_block_non_repeatable' => [ 'json' => true ], + 'repeater_block_max' => [ 'json' => true ], + 'repeater_block_bulk_switch_title' => [ 'json' => true ] + ] + ], + 'table' => [ + 'title' => 'Table', + 'path' => 'repeater/table', + 'fields'=> [ + 'repeater_table' => [ 'json' => true ], + 'repeater_table_non_repeatable' => [ 'json' => true ], + 'repeater_table_max' => [ 'json' => true ] + ] + ] + ] +]); + +$documentation->register_section('elements', [ + 'title' => 'Elements', + 'pages' => [ + 'button' => [ + 'title' => 'Button', + 'path' => 'elements/button', + ], + 'description' => [ + 'title' => 'Description', + 'path' => 'elements/description', + ], + 'label' => [ + 'title' => 'Label', + 'path' => 'elements/label', + ], + 'modal' => [ + 'title' => 'Modal', + 'path' => 'elements/modal', + ], + 'tooltip' => [ + 'title' => 'Tooltip', + 'path' => 'elements/tooltip' + ] + ] +]); + +$documentation->register_section('conditional-logic', [ + 'title' => 'Conditional logic', + 'pages' => [ + 'create' => [ + 'title' => 'Create conditions', + 'path' => 'conditional-logic/create', + 'fields'=> [ + 'conditonal_logic' => [ 'json' => true ], + 'conditonal_logic_modal' => [ 'json' => true ], + ] + ], + 'evaluate' => [ + 'title' => 'Evaluate', + 'path' => 'conditional-logic/evaluate', + ], + ] +]); + +$documentation->register_section('javascript', [ + 'title' => 'JavaScript API', + 'pages' => [ + 'render' => [ + 'title' => 'Render', + 'path' => 'javascript/render' + ], + 'event' => [ + 'title' => 'Events', + 'path' => 'javascript/events' + ], + 'store' => [ + 'title' => 'Store', + 'path' => 'javascript/store' + ], + 'fields' => [ + 'title' => 'Fields', + 'path' => 'javascript/fields' + ], + 'types' => [ + 'title' => 'Custom field type', + 'path' => 'javascript/types', + 'fields' => [ + 'custom-field-example' => [ 'json' => true ] + ] + ] + ] +]); + +$documentation->register_section('field-visibility', [ + 'title' => 'Fields visibility conditions', + 'pages' => [ + 'syntax' => [ + 'title' => 'Syntax', + 'path' => 'visibility/syntax' + ], + 'operators' => [ + 'title' => 'Operators', + 'path' => 'visibility/operators', + ], + 'example' => [ + 'title' => 'Example', + 'path' => 'visibility/example', + 'fields'=>[ + 'visibility-text' => [], + 'visibility-repeater' => [ 'json' => true ] + ] + ] + ] +]); + +$documentation->register_section('dependent-attributes', [ + 'title' => 'Dependent attributes', + 'pages' => [ + 'syntax' => [ + 'title' => 'Syntax', + 'path' => 'dependent-attributes/syntax', + 'fields' => [] + ], + 'exmaple' => [ + 'title' => 'Example', + 'path' => 'dependent-attributes/example', + 'fields'=> [ + 'dependent-color-switch' => [], + 'dependent-color-picker' => [], + 'dependent-attribute-repeater' => [ 'json' => true ] + ] + ] + ] +]); + +$documentation->register_section('dynamic-values', [ + 'title' => 'Dynamic values', + 'pages' => [ + 'usage' => [ + 'title' => 'Usage', + 'path' => 'dynamic-values/usage', + ], + 'registration' => [ + 'title' => 'Registration', + 'path' => 'dynamic-values/registration' + ], + 'list' => [ + 'title' => 'List', + 'path' => 'dynamic-values/list' + ], + 'examples' => [ + 'title' => 'Examples', + 'path' => 'dynamic-values/examples', + 'fields' => [ + 'dynamic-text' => [ 'json' => true ], + 'dynamic-text-replace' => [ 'json' => true ], + 'dynamic-color' => [], + 'dynamic-date' => [], + 'dynamic-number' => [], + ] + ] + ] +]); + +$documentation->register_section('context', [ + 'title' => 'Style contex', + 'pages' => [ + 'usage' => [ + 'title' => 'Usage', + 'path' => 'context/usage', + ] + ] +]); diff --git a/example/templates/conditional-logic/create.php b/example/templates/conditional-logic/create.php new file mode 100644 index 00000000..e40498b4 --- /dev/null +++ b/example/templates/conditional-logic/create.php @@ -0,0 +1,72 @@ +

Render conditonal panel

+ +Conditions can be saved using the conditional panel field type. + +The data saved by this field type can then retrieve and be evaluated (more about this in the evaluate page). + +

Simple example

+ +Here is how the conditional panel looks like, and the associated code: + +
+ render_field('conditonal_logic', [ + 'type' => 'conditional_panel', + 'value' => $fields->fetch_value('conditonal_logic'), + 'dynamic_categories' => [ 'post' ], + 'operators' => [ + '_eq' => 'Is', + '_neq' => 'Is not', + '_lt' => 'Less than', + '_gt' => 'Greater than', + ] + ]); ?> +
+ +start_code('php') ?> +$fields->render_field('conditonal_logic', [ + 'type' => 'conditional_panel', + 'value' => $fields->fetch_value('conditonal_logic'), + 'dynamic_categories' => [ 'post', 'user', 'general' ], + 'operators' => [ + '_eq' => 'Is', + '_neq' => 'Is not', + '_lt' => 'Less than', + '_gt' => 'Greater than', + ] +]); +end_code() ?> + +
fetch_value('conditonal_logic')) ?>
+ +
+ +
+ +

Example with a modal

+ +It is possible to wrap the conditional panel inside a modal, by using the use_modal parameter. + +Here is an example with a modal: + +
+ render_field('conditonal_logic_modal', [ + 'type' => 'conditional_panel', + 'use_modal' => true, + 'value' => $fields->fetch_value('conditonal_logic_modal'), + ]); ?> +
+ +start_code('php') ?> +$fields->render_field('conditonal_logic_modal', [ + 'type' => 'conditional_panel', + 'use_modal' => true, + 'value' => $fields->fetch_value('conditonal_logic_modal'), + // 'dynamic_categories' => [ 'post', 'user', 'general' ], +]); +end_code() ?> + +
fetch_value('conditonal_logic_modal')) ?>
+ +
+ +
diff --git a/example/templates/conditional-logic/evaluate.md b/example/templates/conditional-logic/evaluate.md new file mode 100644 index 00000000..2b9c582d --- /dev/null +++ b/example/templates/conditional-logic/evaluate.md @@ -0,0 +1 @@ +_This part is not documented for now_ diff --git a/example/templates/context/usage.md b/example/templates/context/usage.md new file mode 100644 index 00000000..9d7f263a --- /dev/null +++ b/example/templates/context/usage.md @@ -0,0 +1,66 @@ +#### What is "Context" in Tangible Fields? + +"Context" in Tangible Fields is a way to define a specific styling context for your fields. You can switch between different styling contexts to adapt the appearance of your form fields to the specific needs of your project or platform. The library provides a list of predefined contexts, and you can select the one that best fits your requirements. + +Multiple contextes can be used on the same page without conflicts. + +#### Predefined Contexts + +Tangible Fields comes with the following predefined contexts: + +- default - The default styling context, with minimal styling +- wp - Styling for the WordPress admin environments +- elementor - Styling for use with the Elementor page builder +- beaver-builder - Styling for use with the Beaver Builder page builder + +#### Usage - PHP + +If no context is set, the default context will be used. + +However it's recommanded to always explicitly set the context even if it uses the default, as if a context is set somewhere else and you don't set it the defined context will be used for you fields as well. + +You can set the desired context for your fields by calling the `set_context` function before rendering your fields. Here's how you can use it: +```php +$fields->set_context('context_name'); + +echo $fields->render_field( 'name-1', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('name-1'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' +]); + +echo $fields->render_field( 'name-2', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('name-2'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' +]); +``` + +Replace `context_name` with the name of the context you want to use ('wp', 'elementor', 'beaver-builder', or 'default'). + +#### Usage - JavaScript + +When creating fields dynamically using JavaScript, you need to manually enqueue the stylesheet for the associated context since it won't be automatically added to the page. +```php +// If not specified, will load default context +$fields->enqueue(); + +// Will load default and WP context +$fields->enqueue([ + 'context' => ['default', 'wp'] +]); +``` + +Here's how you can set the context on a field created from JavaScript: +```javascript +tangibleFields.render({ + type : 'text', + label : 'Text', + name : 'field-name', + context : 'wp' +}) +``` diff --git a/example/templates/dependent-attributes/example.php b/example/templates/dependent-attributes/example.php new file mode 100644 index 00000000..aac5ab4d --- /dev/null +++ b/example/templates/dependent-attributes/example.php @@ -0,0 +1,121 @@ +

Color opacity

+ +

The switch will enable or disable the opacity in the color picker:

+ +
+ render_field('dependent-color-switch', [ + 'type' => 'switch', + 'value' => $fields->fetch_value('dependent-color-switch'), + 'value_on' => true, + 'value_off' => false + ]); ?> +
+ +
+ render_field('dependent-color-picker', [ + 'type' => 'color_picker', + 'enable_opacity' => '{{dependent-color-switch}}', + 'value' => $fields->fetch_value('dependent-color-picker'), + 'dependent' => true, + ]); ?> +
+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('dependent-color-switch', [
+      'type'      => 'switch',
+      'value'     => $fields->fetch_value('dependent-color-switch'),
+      'value_on'  => true,
+      'value_off' => false
+    ]);
+
+    echo $fields->render_field('dependent-color-picker', [
+      'type'           => 'color_picker',
+      'enable_opacity' => '{{dependent-color-switch}}',
+      'value'          => $fields->fetch_value('dependent-color-picker'),
+      'dependent'      => true,
+    ]);
+  
+
+ +

Repeater

+ +

The first select of a row will the post types of the post in the combo box:

+ +
+ render_field('dependent-attribute-repeater', [ + 'type' => 'repeater', + 'layout' => 'table', + 'value' => $fields->fetch_value('dependent-attribute-repeater'), + 'sub_fields' => [ + [ + 'type' => 'select', + 'label' => 'Post types', + 'name' => 'field-post-type', + 'choices' => array_reduce( + get_post_types([], 'objects'), + function($post_types, $item) { + $post_types[ $item->name ] = $item->labels->singular_name; + return $post_types; + }, + [] + ) + ], + [ + 'label' => 'Select a post', + 'description'=> '', + 'name' => 'post-select', + 'type' => 'combo_box', + 'is_async' => true, + 'dependent' => true, + 'search_url' => get_rest_url() . 'wp/v2/search', + 'async_args' => [ + 'subtype' => '{{field-post-type}}' + ], + ] + ] + ]); ?> +
+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('dependent-attribute-repeater', [
+      'type'       => 'repeater',
+      'layout'     => 'table',
+      'value'      => $fields->fetch_value('dependent-attribute-repeater'),
+      'sub_fields' => [
+        [
+          'type'    => 'select',
+          'label'   => 'Post types',
+          'name'    => 'field-post-type',
+          'choices' => array_reduce(
+            get_post_types([], 'objects'),
+            function($post_types, $item) {
+              $post_types[ $item->name ] = $item->labels->singular_name;
+              return $post_types;
+            },
+            []
+          )
+        ],
+        [
+          'label'      => 'Select a post',
+          'description'=> '',
+          'name'       => 'post-select',
+          'type'       => 'combo_box',
+          'is_async'   => true,
+          'dependent'  => true,
+          'search_url' => get_rest_url() . 'wp/v2/search',
+          'async_args' => [
+            'subtype' => '{{field-post-type}}'
+          ],
+        ]
+      ]
+    ]);
+  
+
diff --git a/example/templates/dependent-attributes/syntax.php b/example/templates/dependent-attributes/syntax.php new file mode 100644 index 00000000..48d53ecf --- /dev/null +++ b/example/templates/dependent-attributes/syntax.php @@ -0,0 +1,41 @@ +

The "Dependent Values" feature allows you to establish dynamic relationships between a 2 fields, where an attribute of one field is dependent on the value of another field.

+

This feature is particularly useful when you want to create interactive and context-aware fields that adapt to user selections.

+ +

Usage

+ +

To set up dependent values, follow these steps:

+
    +
  • In your field definitions, set the dependent attribute to true
  • +
  • In the same field definition, use the notation {{field-name}} to indicate that a given attribut depends on another field's value. The field-name should be replaced with the actual name of the field you want to reference. You can use multiple depedent values coming from different fields inside on same field.
  • +
  • If the field value we depend of is an object, it's possible to access to an attribute by using the following syntax: {{field-name.attribute}}. For example in the case of an async combobox, the field value will be a object like this one: { value: '1', label: 'Label' }. It would be possible to access only the object or the label by doing the following: {{field-name.value}} and {{field-name.label}}.
  • +
+ +

In this example, the label and the description the last field will be the value of the other 2 fields:

+
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('label-field', [
+      'label'      => 'Set the label of the last field',
+      'type'       => 'text',
+      'value'      => $fields->fetch_value('label-field'),
+    ]);
+
+    echo $fields->render_field('description-field', [
+      'label'      => 'Set the description of the last field',
+      'type'       => 'text',
+      'value'      => $fields->fetch_value('description-field'),
+    ]);
+
+    echo $fields->render_field('custom-field', [
+      'label'       => '{{label-field}}',
+      'description' => '{{description-field}}',
+      'type'        => 'text',
+      'dependent'   => true,
+      'value'       => $fields->fetch_value('custom-field'),
+    ]);
+  
+
+ + diff --git a/example/templates/dynamic-values/examples.php b/example/templates/dynamic-values/examples.php new file mode 100644 index 00000000..d367a91b --- /dev/null +++ b/example/templates/dynamic-values/examples.php @@ -0,0 +1,119 @@ +

Text (insert mode)

+ +
+ render_field('dynamic-text', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('dynamic-text'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'dynamic' => true + ]) ?> +
+ +fetch_value('dynamic-text'), + 'Parsed value: ' . $fields->render_value( + $fields->fetch_value('dynamic-text') + ), +); ?> + +
+ +
+ +

Text (replace mode)

+ +
+ render_field('dynamic-text-replace', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('dynamic-text-replace'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'dynamic' => [ 'mode' => 'replace' ] + ]) ?> +
+ +fetch_value('dynamic-text-replace'), + 'Parsed value: ' . $fields->render_value( + $fields->fetch_value('dynamic-text-replace') + ), +); ?> + +
+ +
+ +

Color

+ +
+ render_field('dynamic-color', [ + 'label' => 'Color field', + 'type' => 'color_picker', + 'value' => $fields->fetch_value('dynamic-color'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'dynamic' => true + ]) ?> +
+ +fetch_value('dynamic-color'), + 'Parsed value: ' . $fields->render_value( + $fields->fetch_value('dynamic-color') + ), +); ?> + +
+ +
+ +

Date

+ +
+ render_field('dynamic-date', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'value' => $fields->fetch_value('dynamic-date'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'dynamic' => true + ]) ?> +
+ +fetch_value('dynamic-date'), + 'Parsed value: ' . $fields->render_value( + $fields->fetch_value('dynamic-date') + ), +); ?> + +
+ +
+ +

Number

+ +
+ render_field('dynamic-number', [ + 'label' => 'Number', + 'type' => 'number', + 'value' => $fields->fetch_value('dynamic-number'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'dynamic' => true + ]) ?> +
+ +fetch_value('dynamic-number'), + 'Parsed value: ' . $fields->render_value( + $fields->fetch_value('dynamic-number') + ), +); ?> + +
+ +
diff --git a/example/templates/dynamic-values/list.php b/example/templates/dynamic-values/list.php new file mode 100644 index 00000000..b84ae6ab --- /dev/null +++ b/example/templates/dynamic-values/list.php @@ -0,0 +1,45 @@ +
+ +
+ dynamic_values_categories as $category): ?> + + +
    + +
  • + dynamic_values[ $value ]['label'] ?> +
  • + +
+ +
+ +
+ dynamic_values_categories as $category): ?> + + + + +
+ No dynamic value selected +
+
+ +
diff --git a/example/templates/dynamic-values/registration.md b/example/templates/dynamic-values/registration.md new file mode 100644 index 00000000..67e919e3 --- /dev/null +++ b/example/templates/dynamic-values/registration.md @@ -0,0 +1,126 @@ +#### Register category + +Each category represents a group of dynamic values. +Use the `register_dynamic_value_category` function to define a new category: + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value_category('post', [ + 'label' => 'Post', +]); +``` + +#### Register dynamic values + +Use the `register_dynamic_value` function to register each dynamic value under the desired category. + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value([ + 'category' => 'post', + 'name' => 'post_id', + 'label' => 'Post ID', + 'type' => 'text', + 'description' => 'Return the current post ID, if any', + 'callback' => function($settings, $config) { + return $config['context']['current_post_id']; + }, + 'permission_callback' => '__return_true' +]); +``` + +The function takes an array of options as a parameter. Here are all the possible values: + +- category (required): Slug of the associated category +- name (required): Slug of the dynamic value +- label: If not defined, name will be use as the label +- description: Optional description that will be used both in the documentation and in the dynamic setting form (if there is one for this dynamic value) +- type: The type of data returned by the callback, possible values: + - color + - date + - number + - text + +- callback: Function used to render the dynamic value +- permission_callback: Used to evaluate if the current user can save/parse the dynamic value (see permission section below) +- fields: Used to define settings associated to the dynamic value (see fields section below) + +#### Register dynamic values - Permissions + +There are two types of permissions that needs to be set: + +- permission_callback_store: Callback to evaluate if the current user is alowed to save the dynamic value +- permission_callback_parse: Callback to evaluate if the current user is alowed to parse the dynamic value + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value([ + // ...etc + 'permission_callback_store' => function() { + return in_array('administrator', wp_get_current_user()->roles ?? []); + }, + 'permission_callback_parse' => '__return_true' +]); +``` + +If the permission callback is the same for both the store and parse action, +we can pass only one callback by using `permission_callback` instead: + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value([ + // ...etc + 'permission_callback' => function() { + return is_user_logged_in(); + } +]); +``` + +#### Register dynamic values - Fields + +It's possible to add settings on a dynamic value by passing an array of fields. +The user will be able to set the value for each setting on the dynamic value insertion. + +The values are then passed as parameter of the callback function (as an array), and can be used to modify the render. + +The syntax to define the fields is the same than for regular fields (any exising field type can be used and visibility conditions are supported). + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value([ + 'category' => 'post', + 'name' => 'post_title', + 'label' => 'Post title', + 'type' => 'text', + 'fields' => [ + [ + 'type' => 'select', + 'name' => 'format', + 'label' => 'Format', + 'choices' => [ + 'none' => 'None', + 'lowercase' => 'Lowercase', + 'uppercase' => 'Uppercase' + ] + ] + ], + 'callback' => function($settings, $config) { + + $format = $settings['format'] ?? 'none'; + $post_title = get_the_title( + $config['context']['current_post_id'] + ); + + if( $format === 'lowercase' ) return strtolower($post_title); + if( $format === 'uppercase' ) return strtoupper($post_title); + + return $post_title; + }, + 'permission_callback' => '__return_true' +]); +``` diff --git a/example/templates/dynamic-values/usage.md b/example/templates/dynamic-values/usage.md new file mode 100644 index 00000000..3f40caa5 --- /dev/null +++ b/example/templates/dynamic-values/usage.md @@ -0,0 +1,102 @@ +#### Activate on a field + +To add support for dynamic values on a field, we need to add `'dynamic' => true` in the field definition. + +It will only works with the supported field types (more to come): +- color_picker +- date_picker +- number +- text + +```php +$fields = tangible_fields(); + +$fields->render_field('text-with-dynamic-values', [ + 'label' => 'Text field with dynamic values', + 'type' => 'text', + 'value' => $fields->fetch_value('text-with-dynamic-values'), + 'dynamic' => true +]); +``` + +The `text` field type support dynamic values in 2 ways (there is an example of each on the example page): +- `insert` - This is the default behavior, it allows to insert multiple dynamic value alongside regular text +- `replace` - It allows to either set one dynamic value, or to use regular text (can't mix both) + +If you need to set one type, you can use `mode` parameter like in the following code: +```php +$fields->render_field('text-with-dynamic-values', [ + // ... + 'dynamic' => [ + 'mode' => 'insert' + ] +]); + +$fields->render_field('text-with-dynamic-values', [ + // ... + 'dynamic' => [ + 'mode' => 'replace' + ] +]); +``` + +#### Render a dynamic value + +##### General + +Dynamic values are stored as a string, that looks like this: `[[dynamic_value_name::setting=value::setting2=value2]]` + +To render a string that contains a dynamic value, we use the `render_value` function: +```php +$fields = tangible_fields(); +$value = 'User ID: [[user_id]]' + +// Will output something like "User ID: 1" +$output = $fields->render_value($value); +``` + +For registered fields, it needs to be applied manually after fetching the value: +```php +$fields = tangible_fields(); + +$output = $fields->render_value( + $fields->fetch_value('field_name') +); +``` + +##### Advanced case + +If you want to customize the rendering behavior of dynamic values, you can provide a custom `$config` array as a second parameter. +The `$config` array allows you to set context-specific data or additional options that can influence how dynamic values are rendered. + +```php +// Assuming that current user id is 1, will returns "User ID: 1" +$fields->render_value('User ID: [[user_id]]'); + +// While this will return "User ID: 2" +$fields->render_value('User ID: [[user_id]]', [ + 'context' => [ + 'current_user_id' => 2 + ] +]); +``` +The `$config` parameter is available in dynamic value render callbacks. +When a dynamic value is rendered, the callback function can access the `$config` array, which provides additional contextual information and customization options. + +In your dynamic value render callback function, you can define the `$config` parameter to access the provided configuration. +For example: + +```php +$fields = tangible_fields(); + +$fields->register_dynamic_value([ + 'category' => 'user', + 'name' => 'user_id', + // ...etc + 'callback' => function($settings, $config) { + return $config['context']['current_user_id']; + } +]); +``` + +_Note: More info about dynamic value registration in the next section._ diff --git a/example/templates/elements/button.php b/example/templates/elements/button.php new file mode 100644 index 00000000..d01ec91f --- /dev/null +++ b/example/templates/elements/button.php @@ -0,0 +1,109 @@ +

Example - Button action

+ +
+ register_element('button-action', [ + 'type' => 'button', + 'layout' => 'action', + 'content' => 'Action' + ]) ?> + render_element('button-action') ?> +
+ +

Example - Button danger

+ +
+ register_element('button-danger', [ + 'type' => 'button', + 'layout' => 'danger', + 'content' => 'Danger' + ]) ?> + render_element('button-danger') ?> +
+ +

Example - Button primary

+ +
+ register_element('button-primary', [ + 'type' => 'button', + 'layout' => 'primary', + 'content' => 'Primary' + ]) ?> + render_element('button-primary') ?> +
+ +

Example - Button text action

+ +
+ register_element('button-text-action', [ + 'type' => 'button', + 'layout' => 'text-action', + 'content' => 'Action' + ]) ?> + render_element('button-text-action') ?> +
+ +

Example - Button text danger

+ +
+ register_element('button-text-danger', [ + 'type' => 'button', + 'layout' => 'text-danger', + 'content' => 'Danger' + ]) ?> + render_element('button-text-danger') ?> +
+ +

Example - Button text primary

+ +
+ register_element('button-text-primary', [ + 'type' => 'button', + 'layout' => 'text-primary', + 'content' => 'Primary' + ]) ?> + render_element('button-text-primary') ?> +
+ +

Example - Button simple

+ +
+ register_element('button-simple', [ + 'type' => 'button', + 'content' => 'Simple' + ]) ?> + render_element('button-simple') ?> +
+ +

Code

+ +
+ +start_code('php') ?> + +// Example - PHP + +$fields->register_element('button-name', [ + 'type' => 'button', + 'layout' => 'primary', // Optional: primary, action, danger, text-primary, text-action, text-danger + 'content' => 'Button text' +]); + +echo $fields->render_element('button-name') +end_code() ?> +start_code('javascript') ?> + +// Example - JS + +const component = tangibleFields.render( + { + name : 'button-name', + type : 'button', + layout : 'primary' + content : 'Button text' + } + 'element' +) +end_code() ?> +
+ + diff --git a/example/templates/elements/description.php b/example/templates/elements/description.php new file mode 100644 index 00000000..75581226 --- /dev/null +++ b/example/templates/elements/description.php @@ -0,0 +1,41 @@ +

Example

+ +
+ register_element('description', [ + 'type' => 'description', + 'content' => 'Description text' + ]) ?> + render_element('description') ?> +
+ +

Code

+ +
+ +start_code('php') ?> + +// Example - PHP + +$fields->register_element('description-name', [ + 'type' => 'description', + 'content' => 'Description text' +]); + +echo $fields->render_element('description-name') +end_code() ?> +start_code('javascript') ?> + +// Example - JS + +const component = tangibleFields.render( + { + name : 'description-name', + type : 'description', + content : 'Description text' + } + 'element' +) +end_code() ?> +
+ + diff --git a/example/templates/elements/label.php b/example/templates/elements/label.php new file mode 100644 index 00000000..94341249 --- /dev/null +++ b/example/templates/elements/label.php @@ -0,0 +1,41 @@ +

Example

+ +
+ register_element('label', [ + 'type' => 'label', + 'content' => 'Label' + ]) ?> + render_element('label') ?> +
+ +

Code

+ +
+ +start_code('php') ?> + +// Example - PHP + +$fields->register_element('label-name', [ + 'type' => 'label', + 'content' => 'Label text' +]); + +echo $fields->render_element('label-name') +end_code() ?> +start_code('javascript') ?> + +// Example - JS + +const component = tangibleFields.render( + { + name : 'label-name', + type : 'label', + content : 'Label text' + } + 'element' +) +end_code() ?> +
+ + diff --git a/example/templates/elements/modal.php b/example/templates/elements/modal.php new file mode 100644 index 00000000..39e64eb3 --- /dev/null +++ b/example/templates/elements/modal.php @@ -0,0 +1,55 @@ +

Example

+ +
+ register_element('modal', [ + 'label' => 'Open modal', + 'type' => 'modal', + 'content' => 'Modal', + 'title' => 'Modal title', + 'content' => 'Modal content', + 'confirm_text' => 'Are you sure?', + 'cancel_text' => 'Discard changes', + ]) ?> + render_element('modal') ?> +
+ +

Code

+ +
+ +start_code('php') ?> + +// Example - PHP + +$fields->register_element('modal', [ + 'label' => 'Open modal', + 'type' => 'modal', + 'content' => 'Modal', + 'title' => 'Modal title', + 'content' => 'Modal content', + 'confirm_text' => 'Are you sure?', + 'cancel_text' => 'Discard changes', +]); + +echo $fields->render_element('modal') +end_code() ?> +start_code('javascript') ?> + +// Example - JS + +const component = tangibleFields.render( + { + label : 'Open modal', + type : 'modal', + content : 'Modal', + title : 'Modal title', + content : 'Modal content', + confirmText : 'Are you sure?', + cancelText : 'Discard changes', + } + 'element' +) +end_code() ?> +
+ + diff --git a/example/templates/elements/tooltip.php b/example/templates/elements/tooltip.php new file mode 100644 index 00000000..82db21e0 --- /dev/null +++ b/example/templates/elements/tooltip.php @@ -0,0 +1,152 @@ + + +

Example - Tooltip Button start

+ +
+ register_element('tooltip-button-start', [ + 'type' => 'tooltip', + 'layout' => 'button', + 'label' => 'Button Hover Start', + 'placement' => 'start', + 'content' => 'Tooltip button content, placement on start' + ]) ?> + render_element('tooltip-button-start') ?> +
+ +

Example - Tooltip Button top

+ +
+ register_element('tooltip-button-top', [ + 'type' => 'tooltip', + 'layout' => 'button', + 'label' => 'Button Hover Top', + 'placement' => 'top', + 'content' => 'Tooltip button content, placement on top' + ]) ?> + render_element('tooltip-button-top') ?> +
+ +

Example - Tooltip Button bottom

+ +
+ register_element('tooltip-button-bottom', [ + 'type' => 'tooltip', + 'layout' => 'button', + 'label' => 'Button Hover Bottom', + 'placement' => 'bottom', + 'theme' => 'dark', + 'content' => 'Tooltip button content, placement on bottom' + ]) ?> + render_element('tooltip-button-bottom') ?> +
+ +

Example - Tooltip Button end

+ +
+ register_element('tooltip-button-end', [ + 'type' => 'tooltip', + 'layout' => 'button', + 'label' => 'Button Hover End', + 'placement' => 'end', + 'theme' => 'dark', + 'content' => 'Tooltip button content, placement on end' + ]) ?> + render_element('tooltip-button-end') ?> +
+ + + +

Example - Tooltip Text start

+ +
+ register_element('tooltip-text-start', [ + 'type' => 'tooltip', + 'layout' => 'text', + 'label' => 'Text Hover Start', + 'placement' => 'start', + 'theme' => 'dark', + 'content' => 'Tooltip text content, placement on start' + ]) ?> + render_element('tooltip-text-start') ?> +
+ +

Example - Tooltip Text top

+ +
+ register_element('tooltip-text-top', [ + 'type' => 'tooltip', + 'layout' => 'text', + 'label' => 'Text Hover Top', + 'placement' => 'top', + 'theme' => 'dark', + 'content' => 'Tooltip text content, placement on top' + ]) ?> + render_element('tooltip-text-top') ?> +
+ +

Example - Tooltip Text bottom

+ +
+ register_element('tooltip-text-bottom', [ + 'type' => 'tooltip', + 'layout' => 'text', + 'label' => 'Text Hover Bottom', + 'placement' => 'bottom', + 'content' => 'Tooltip text content, placement on bottom' + ]) ?> + render_element('tooltip-text-bottom') ?> +
+ +

Example - Tooltip Text end

+ +
+ register_element('tooltip-text-end', [ + 'type' => 'tooltip', + 'layout' => 'text', + 'label' => 'Text Hover End', + 'placement' => 'end', + 'content' => 'Tooltip text content, placement on end' + ]) ?> + render_element('tooltip-text-end') ?> +
+ +

Code

+ +
+ + start_code('php') ?> + + // Example - PHP + + $fields->register_element('tooltip-name', [ + 'type' => 'tooltip', + 'layout' => 'button', // Optional: 'button', 'text' + 'button_props' => [ 'type' => 'primary' ], // Optional, anything supported by the button element + 'label' => 'Top' + 'placement' => 'top', // Optional: 'start', 'top', 'bottom', 'end' + 'theme' => 'light', // Optional: 'dark', 'light' + 'content' => 'Tooltip content, placement on top' + ); + + echo $fields->render_element('tooltip-name') + + end_code() ?> + start_code('javascript') ?> + + // Example - JS + + const component = tangibleFields.render( + { + name : 'tooltip-name', + type : 'tooltip', + layout : 'button', // Optional: 'button', 'text' + buttonProps : { onPress: () => console.log('Button pressed') } // Optional, anything supported by the button element + label : 'Top', + placement : 'top', // Optional: 'start', 'top', 'bottom', 'end' + theme : 'light', // Optional: 'dark', 'light' + content : 'Tooltip content, placement on top' + } + 'element' + ) + end_code() ?> +
diff --git a/example/templates/fields/accordion.php b/example/templates/fields/accordion.php new file mode 100644 index 00000000..bf3c7e32 --- /dev/null +++ b/example/templates/fields/accordion.php @@ -0,0 +1,92 @@ +An accordions can contain any other field (it works like a field group), an the global value will be saved as a json object. + +

Example

+ +
+ render_field('accordion', [ + 'label' => 'Accordion', + 'type' => 'accordion', + 'value' => $fields->fetch_value('accordion'), + 'fields' => [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ],[ + 'label' => 'Text', + 'type' => 'dimensions', + 'name' => 'dimension_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Example with activation toggle

+ +The value of the toggle will be saved in the JSON object, using the name "enabled". + +
+ render_field('accordion-with-switch', [ + 'label' => 'Accordion', + 'type' => 'accordion', + 'value' => $fields->fetch_value('accordion-with-switch'), + 'use_switch' => true, + 'fields' => [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ],[ + 'label' => 'Text', + 'type' => 'dimensions', + 'name' => 'dimension_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('accordion'), + $fields->fetch_value('accordion-with-switch') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'      => 'Accordion',
+      'type'       => 'accordion',
+      'value'      => $fields->fetch_value('name'),
+      'use_switch' => true, // Optional, default false
+      'fields'     => [
+
+        // Can be any type of field, just make sure to add a name
+
+        [
+          'label'   => 'Text',
+          'type'    => 'text',
+          'name'    => 'text_name',
+        ],[
+          'label'   => 'Text',
+          'type'    => 'dimensions',
+          'name'    => 'dimension_name',
+        ]
+      ]
+    ]);
+  
+
+ diff --git a/example/templates/fields/alignment-matrix.php b/example/templates/fields/alignment-matrix.php new file mode 100644 index 00000000..226046ba --- /dev/null +++ b/example/templates/fields/alignment-matrix.php @@ -0,0 +1,36 @@ +

Example

+ +
+ render_field('alignment_matrix', [ + 'label' => 'Alignment matrix', + 'type' => 'alignment_matrix', + 'description' => 'Description', + 'value' => $fields->fetch_value('alignment_matrix'), + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('alignment_matrix') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('alignment_matrix', [
+      'label'      => 'Alignment Matrix',
+      'type'       => 'alignment_matrix',
+      'description'=> 'Description',
+      'value'      => $fields->fetch_value('alignment_matrix'),
+    ]);
+  
+
diff --git a/example/templates/fields/angle-picker.php b/example/templates/fields/angle-picker.php new file mode 100644 index 00000000..ef626353 --- /dev/null +++ b/example/templates/fields/angle-picker.php @@ -0,0 +1,41 @@ +

Angle Picker

+ +Choose an angle by directly inserting the desired angle in the text field or with the mouse by dragging the angle +indicator inside a circle. + +

Example with forced linked values

+ +
+ render_field('angle_picker', [ + 'label' => 'Angle Picker field', + 'type' => 'angle_picker', + 'description' => 'Description', + 'value' => $fields->fetch_value('angle_picker'), + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('angle_picker') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Angle Picker field',
+      'type'        => 'angle_picker',
+      'description' => 'Description',
+      'value'       => $fields->fetch_value('name'),
+    ]);
+  
+
diff --git a/example/templates/fields/border.php b/example/templates/fields/border.php new file mode 100644 index 00000000..f10fecf8 --- /dev/null +++ b/example/templates/fields/border.php @@ -0,0 +1,83 @@ +

Example with linked values and opacity

+ +
+ render_field('border', [ + 'label' => 'Border field', + 'type' => 'border', + 'description' => 'Description', + 'enable_opacity'=> true, + 'format' => 'rgba', + 'units' => ['px', 'vh', '%', 'vw'], + 'value' => $fields->fetch_value('border') + ]) ?> +
+ +
+ +
+ +

Example without linked values and opacity

+ +
+ render_field('border_unlinked', [ + 'label' => 'Border field', + 'type' => 'border', + 'description' => 'Description', + 'enable_opacity'=> false, + 'linked' => false, + 'format' => 'rgba', + 'units' => ['px', 'vh', '%', 'vw'], + 'value' => $fields->fetch_value('border_unlinked') + ]) ?> +
+ +
+ +
+ +

Example with forced linked values and opacity

+ +
+ render_field('border_linked', [ + 'label' => 'Border field', + 'type' => 'border', + 'description' => 'Description', + 'enable_opacity'=> true, + 'linked' => true, + 'format' => 'hex', + 'units' => ['px', 'vh', '%', 'vw'], + 'value' => $fields->fetch_value('border_linked') + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('border'), + $fields->fetch_value('border_unlinked'), + $fields->fetch_value('border_linked') +); ?> + +

Code

+ +
+  
+  $fields = tangible_fields();
+  render_registation_message(); ?>
+
+  echo $fields->render_field('name', [
+    'type'            => 'border',
+    'value'           => $fields->fetch_value('name'),
+    'label'           => 'Border',
+    'enable_opacity'  => true, // Optional, default true
+    'linked'          => 'toggle', // Optional, default toggle (use true or false to force value)
+    'format'          => 'rgba', // hex, rgb, hsl
+    'units'           => ['px', 'vh', '%', 'vw'], // Optional, default px
+    'description'     => 'Example description' 
+  ]);
+  
+
diff --git a/example/templates/fields/button-group.php b/example/templates/fields/button-group.php new file mode 100644 index 00000000..ef72d88a --- /dev/null +++ b/example/templates/fields/button-group.php @@ -0,0 +1,82 @@ +Buttons can contain both dashicon or text. + +

Example with Dashicon

+ +
+ render_field('button_group_dashicon', [ + 'label' => 'Button group field', + 'type' => 'button_group', + 'description' => 'Description', + 'use_dashicon'=> true, + 'value' => $fields->fetch_value('button_group_dashicon'), + 'choices' => [ + 'left' => 'editor-alignleft', + 'center' => 'editor-aligncenter', + 'right' => 'editor-alignright', + ] + ]) ?> +
+ +
+ +
+ +

Example with text

+ +
+ render_field('button_group', [ + 'label' => 'Button group field', + 'type' => 'button_group', + 'description' => 'Description', + 'value' => $fields->fetch_value('button_group'), + 'choices' => [ + 'one' => 'One', + 'two' => 'Two', + 'three' => 'Three', + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('button_group_dashicon'), + $fields->fetch_value('button_group') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Button group field',
+      'type'        => 'button_group',
+      'description' => 'Description',
+      'value'       => $fields->fetch_value('name'),
+
+      // Dashicon
+
+      'use_dashicon'=> true
+      'choices'     => [
+        'left'   => 'editor-alignleft',
+        'center' => 'editor-aligncenter',
+        'right'  => 'editor-alignright',
+      ],
+      
+        // Text
+
+      'choices' => [
+        'one'   => 'One',
+        'two'   => 'Two',
+        'three' => 'Three',
+      ],
+    ]);
+   
+
diff --git a/example/templates/fields/checkbox.php b/example/templates/fields/checkbox.php new file mode 100644 index 00000000..f74641f6 --- /dev/null +++ b/example/templates/fields/checkbox.php @@ -0,0 +1,37 @@ +

Example

+ +
+ render_field('checkbox', [ + 'label' => 'Checkbox field', + 'type' => 'checkbox', + 'value' => $fields->fetch_value('checkbox'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('checkbox') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Checkbox field',
+      'type'        => 'checkbox',
+      'value'       => $fields->fetch_value('name'),
+      'description' => 'Example description'
+    ]);
+  
+
diff --git a/example/templates/fields/code.php b/example/templates/fields/code.php new file mode 100644 index 00000000..6a0a08ef --- /dev/null +++ b/example/templates/fields/code.php @@ -0,0 +1,38 @@ +

Example

+ +
+ render_field('code', [ + 'label' => 'Code', + 'type' => 'code', + 'value' => $fields->fetch_value('code'), + 'description' => 'Example description', + ]) + ?> +
+ +
+ +
+ +

Value

+ +fetch_value('code') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+    
+    $fields->render_field('code', [
+      'label'       => 'Code',
+      'type'        => 'code',
+      'value'       => $fields->fetch_value('code'),
+      'description' => 'Example description',
+    ])
+  
+
diff --git a/example/templates/fields/color-picker.php b/example/templates/fields/color-picker.php new file mode 100644 index 00000000..b6713371 --- /dev/null +++ b/example/templates/fields/color-picker.php @@ -0,0 +1,69 @@ +Possible format: +
    +
  • rgb
  • +
  • rgba
  • +
  • hex
  • +
  • hsl
  • +
+ +

Example with opacity

+ +
+ render_field('color_opacity', [ + 'type' => 'color_picker', + 'value' => $fields->fetch_value('color_opacity'), + 'label' => 'Color', + 'enable_opacity' => true, + 'format' => 'rgba', + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example without opacity

+ +
+ render_field('color', [ + 'type' => 'color_picker', + 'value' => $fields->fetch_value('color'), + 'label' => 'Color', + 'enable_opacity' => false, + 'format' => 'hex', + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('color_opacity'), + $fields->fetch_value('color') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'type'            => 'color_picker',
+      'value'           => $fields->fetch_value('name'),
+      'label'           => 'Color',
+      'enable_opacity'  => true, // Optional, default true
+      'format'          => 'rgba', // hex, rgb, hsl
+      'placeholder'     => 'Example placeholder',
+      'description'     => 'Example description' 
+    ]);
+   
+
diff --git a/example/templates/fields/combo-box-async.php b/example/templates/fields/combo-box-async.php new file mode 100644 index 00000000..267ef38f --- /dev/null +++ b/example/templates/fields/combo-box-async.php @@ -0,0 +1,177 @@ +While the control looks the same regardless of if it uses static or async data, the returned value will be different.
+In the case of an async combobox, it will be a json object with both the value and the labe (instead of a string with the value). + +

Map results

+ +

The optional map_results attribute allows you to configure how data from asynchronous sources is mapped to the id and title attributes of the ComboBox options.

+

This mapping is crucial if the endpoint/ajax action you are using doesn't return objects with a title and an id keys.

+ +

For example, if the returned data for a given endpoint is this JSON:

+
+  
+    [
+      { label: 'First', uuid: '1' },
+      { label: 'Second', uuid: '2' }
+    ]
+  
+
+ +

We will want to use label as the title, and uuid as the id.

+

In order to do that, we can will have to set map_results like this:

+
+  
+    echo $fields->render_field('name', [
+      'type'  => 'combo_box',
+      // ...
+      'map_results' => [
+        'id'    => 'uuid'
+        'title' => 'label'
+      ]
+    ]);
+  
+
+ +

It is also possible to get data from an object, for example with this response:

+
+  
+    [
+      { 
+        uuid: '1',
+        data: {
+          title: 'Fist',
+          content: 'First content'
+        } 
+      },
+      { 
+        uuid: '2',
+        data: {
+          title: 'Second',
+          content: 'Second content'
+        } 
+      }
+  ] 
+  
+
+ +

We can use the following map_results:

+
+  
+    echo $fields->render_field('name', [
+      'type'  => 'combo_box',
+      // ...
+      'map_results' => [
+        'id'    => 'uuid'
+        'title' => [
+          'key'       => 'data',
+          'attribute' => 'title',
+        ]
+      ]
+    ]);
+  
+
+ +

Currently, it won't work with nested objects.

+ +

Example with async loading (using a fetch url)

+ +
+ render_field('combobox_async_rest', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox_async_rest'), + 'label' => 'Categories list combobox', + 'is_async' => true, + 'search_url' => get_rest_url() . 'wp/v2/search', + 'async_args' => [ + 'subtype' => 'post' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with async loading (using ajax module from the framework)

+ +
+ render_field('combobox_async_ajax', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox_async_ajax'), + 'label' => 'Categories list combobox', + 'is_async' => true, + 'ajax_action' => 'tangible_field_select_post', // @see ../ajax/index.php + 'async_args' => [ + 'post_type' => 'post,page' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with async loading and multiple values

+ +
+ render_field('combobox_async_multiple', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox_async_multiple'), + 'label' => 'Categories list combobox', + 'is_async' => true, + 'multiple' => true, + 'ajax_action' => 'tangible_field_select_post', // @see ../ajax/index.php + 'async_args' => [ + 'post_type' => 'post,page' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('combobox_async_rest'), + $fields->fetch_value('combobox_async_ajax'), + $fields->fetch_value('combobox_async_multiple') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'type'  => 'combo_box',
+      'value' => $fields->fetch_value('name'),
+      'label' => 'Categories list combobox',
+
+      // Async list (fetch url)
+      'is_async'   => true,
+      'search_url' => get_rest_url() . 'wp/v2/search',
+      'async_args' => [
+        'subtype' => 'post'
+      ],
+
+      // Async list (framework ajax module, @see https://docs.tangible.one/modules/plugin-framework/ajax/)
+      'is_async'   => true,
+      'ajax_action'=> 'ajax_action_name',
+      'async_args' => [
+        'post_type' => 'post'
+      ],
+
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+    ]);
+   
+
diff --git a/example/templates/fields/combo-box.php b/example/templates/fields/combo-box.php new file mode 100644 index 00000000..7cd45af2 --- /dev/null +++ b/example/templates/fields/combo-box.php @@ -0,0 +1,133 @@ +Might be renamed to "search" in the future. + +

Example

+ +
+ render_field('combobox', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox'), + 'label' => 'Categories list combobox', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with multiple values

+ +
+ render_field('combobox_multiple', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox_multiple'), + 'label' => 'Categories list combobox', + 'multiple' => true, + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with categories

+ +
+ render_field('combobox_categories', [ + 'type' => 'combo_box', + 'value' => $fields->fetch_value('combobox_categories'), + 'label' => 'Categories list combobox', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2 ' => 'Test2' + ] + ], + [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('combobox'), + $fields->fetch_value('combobox_multiple'), + $fields->fetch_value('combobox_categories'), +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'type'  => 'combo_box',
+      'value' => $fields->fetch_value('name'),
+      'label' => 'Categories list combobox',
+
+      // Simple list
+      'choices' => [
+        'test1' => 'Test1'
+        'test2' => 'Test2'
+        'test3' => 'Test3'
+        'test4' => 'Test4'
+      ],
+      
+      // If multiple
+      'multiple' => true,
+
+      // List with categories
+      'choices' => [
+        [
+          'name'    => 'Category 1',
+          'choices' => [ 
+            'test1' => 'Test1'
+            'test2' => 'Test2'
+          ]
+        ],
+        [
+          'name'    => 'Category 2',
+          'choices' => [
+            'test3' => 'Test3',
+            'test4' => 'Test4'
+          ]
+        ]
+      ],
+
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+    ]);
+   
+
diff --git a/example/templates/fields/date-picker.php b/example/templates/fields/date-picker.php new file mode 100644 index 00000000..8b6d312c --- /dev/null +++ b/example/templates/fields/date-picker.php @@ -0,0 +1,114 @@ +Currently this field as some issues with focus (the popup will stay open until we chose a value). + +It's also not possible to set a time yet, only the date. + +

Example

+ +
+ render_field('date', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'description' => 'Description', + 'value' => $fields->fetch_value('date'), + ]) ?> +
+ +
+ + + +

Example of Future Dates only

+ +
+ render_field('date_future_only', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'description' => 'Description', + 'future_only' => true, + 'value' => $fields->fetch_value('date_future_only'), + ]) ?> +
+ +
+ + + +

Example of Date Range

+ +
+ render_field('date_range', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'description' => 'Description', + 'date_range' => true, + 'value' => $fields->fetch_value('date_range'), + ]) ?> +
+ +
+ + + +

Example of Date Range with Multiple Month View

+ +
+ render_field('multi_month', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'description' => 'Description', + 'date_range' => true, + 'multi_month' => 3, + 'value' => $fields->fetch_value('multi_month'), + ]) ?> +
+ +
+ + + +

Example of Date Range with Calendar Buttons / Presets

+ +
+ render_field('date_presets', [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'description' => 'Description', + 'date_range' => true, + 'multi_month' => 2, + 'date_presets'=> true, + 'value' => $fields->fetch_value('date_presets'), + ]) ?> +
+ +
+ + + +

Value

+ +fetch_value('date'), + $fields->fetch_value('date_future_only'), + $fields->fetch_value('date_range'), + $fields->fetch_value('multi_month'), + $fields->fetch_value('date_presets'), +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label' => 'Date field',
+      'type'  => 'date_picker',
+      'future_only'      => true,   // to restrict selection to future dates only
+      'date_range'       => true,   // to enable multi-select of date ranges
+      'multi_month'      => 2,      // to display multiple months in a single pop-over, to use this, you must enable date_range
+      'date_presets'     => true,   // to display calendar buttons/presets such as ( Today, Last Week, This Month, and Last Month), to use this, you must enable date_range
+      'value' => $fields->fetch_value('name'),
+    ]);
+   
+
diff --git a/example/templates/fields/dimensions.php b/example/templates/fields/dimensions.php new file mode 100644 index 00000000..09007151 --- /dev/null +++ b/example/templates/fields/dimensions.php @@ -0,0 +1,75 @@ +

Example linked values

+ +
+ render_field('dimensions', [ + 'label' => 'Dimensions field', + 'type' => 'dimensions', + 'description' => 'Description', + 'units' => [ 'vh','%', 'vw' ], + 'value' => $fields->fetch_value('dimensions'), + ]) ?> +
+ +
+ +
+ +

Example without linked values

+ +
+ render_field('dimensions_not_linked', [ + 'label' => 'Dimensions field', + 'type' => 'dimensions', + 'description' => 'Description', + 'units' => [ 'vh','%', 'vw' ], + 'linked' => false, + 'value' => $fields->fetch_value('dimensions_not_linked'), + ]) ?> +
+ +
+ +
+ +

Example with forced linked values

+ +
+ render_field('dimensions_linked', [ + 'label' => 'Dimensions field', + 'type' => 'dimensions', + 'description' => 'Description', + 'units' => [ 'px','vw' ], + 'linked' => true, + 'value' => $fields->fetch_value('dimensions_linked'), + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('dimensions'), + $fields->fetch_value('dimensions_not_linked'), + $fields->fetch_value('dimensions_linked') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Dimensions field',
+      'type'        => 'dimensions',
+      'description' => 'Description',
+      'units'       => [ 'px','vw' ], // Optional, default px
+      'linked'      => 'toggle', // Optional, default toggle (use true or false to force value)
+      'value'       => $fields->fetch_value('name'),
+    ]);
+   
+
diff --git a/example/templates/fields/field-group.php b/example/templates/fields/field-group.php new file mode 100644 index 00000000..c98329df --- /dev/null +++ b/example/templates/fields/field-group.php @@ -0,0 +1,61 @@ +A field group can contain any other field, an the global value will be saved as a json object. + +

Example

+ +
+ render_field( 'field_group', [ + 'label' => 'Field group', + 'type' => 'field_group', + 'value' => $fields->fetch_value('field_group'), + 'fields'=> [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ],[ + 'label' => 'Text', + 'type' => 'dimensions', + 'name' => 'dimension_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('field_group') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'  => 'Text field',
+      'type'   => 'field_group',
+      'value'  => $fields->fetch_value('name'),
+      'fields' => [
+
+        // Can be any type of field, just make sure to add a name
+
+        [
+          'label' => 'Text',
+          'type'  => 'text',
+          'name'  => 'text_name',
+        ],[
+          'label' => 'Text',
+          'type'  => 'dimensions',
+          'name'  => 'dimension_name',
+        ]
+      ]
+    ]);
+  
+
diff --git a/example/templates/fields/file.php b/example/templates/fields/file.php new file mode 100644 index 00000000..1da99372 --- /dev/null +++ b/example/templates/fields/file.php @@ -0,0 +1,136 @@ +The saved value is an attachment ID.
+An important note is that all files uploaded will stay, regardless of if the field value is saved or not.
+By default, all the mime types from this function are allowed: get_allowed_mime_types(). + +

Example

+ +
+ render_field('file', [ + 'label' => 'File', + 'type' => 'file', + 'description' => 'Description', + 'value' => $fields->fetch_value('file'), + ]) ?> +
+ +
+ +
+ +

Example of using stock file input field

+ +
+ render_field('file_input', [ + 'label' => 'File', + 'type' => 'file', + 'description' => 'Description', + 'value' => $fields->fetch_value('file_input'), + 'wp_media' => false + ]) ?> +
+ +
+ +
+ +

Example of wp_media enabled and shortcut for mimetypes

+ +
+ render_field('file_wp_media', [ + 'label' => 'File', + 'type' => 'file', + 'description' => 'Description', + 'value' => $fields->fetch_value('file_wp_media'), + 'mime_types' => [ + 'video', + 'image', + ] + ]) ?> +
+ +
+ +
+ +

Example with image only

+ +render_field('file_image', [ + 'label' => 'File', + 'type' => 'file', + 'description' => 'Description', + 'value' => $fields->fetch_value('file_image'), + 'mime_types' => [ + 'image/jpeg', + 'image/gif', + 'image/png', + 'image/bmp', + 'image/tiff', + 'image/webp', + 'image/x-icon', + 'image/heic' + ] +]) ?> + +
+ +
+ +

Example with a limit of 2 uploads

+ +render_field('file_limited', [ + 'label' => 'File', + 'type' => 'file', + 'description' => 'Description', + 'value' => $fields->fetch_value('file_limited'), + 'max_upload' => 2 +]) ?> + +
+ +
+ +

Value

+ +fetch_value('file'), + $fields->fetch_value('file_input'), + $fields->fetch_value('file_wp_media'), + $fields->fetch_value('file_image'), + $fields->fetch_value('file_limited') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+    
+    echo $fields->render_field('name', [
+      'label'       => 'Upload field',
+      'type'        => 'file',
+      'description' => 'Description',
+      'value'       => $fields->fetch_value('name'),
+      'max_upload'  => 5,
+      // You can disable wp_media to use the file input field instead of WP media.
+      'wp_media'    => false,
+      'mime_types'  => [
+        // you can include them by shortcut or by specific mime type
+        'video',
+        'image',
+        'text',
+        'application',
+        'audio',
+        'image/jpeg', 
+        'image/gif', 
+        'image/png', 
+        'image/bmp', 
+        'image/tiff', 
+        'image/webp', 
+        'image/x-icon', 
+        'image/heic'
+      ]
+    ]);
+   
+
+ diff --git a/example/templates/fields/gallery.php b/example/templates/fields/gallery.php new file mode 100644 index 00000000..127a06dd --- /dev/null +++ b/example/templates/fields/gallery.php @@ -0,0 +1,38 @@ +

Example

+ +
+ render_field('gallery', [ + 'label' => 'Gallery field', + 'type' => 'gallery', + 'value' => $fields->fetch_value('gallery'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('gallery') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Gallery field',
+      'type'        => 'gallery',
+      'value'       => $fields->fetch_value('name'),
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+    ]);
+  
+
diff --git a/example/templates/fields/gradient.php b/example/templates/fields/gradient.php new file mode 100644 index 00000000..0210c959 --- /dev/null +++ b/example/templates/fields/gradient.php @@ -0,0 +1,38 @@ +

Example

+ +
+ render_field('gradient', [ + 'type' => 'gradient', + 'value' => $fields->fetch_value('gradient'), + 'label' => 'Gradient', + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('gradient') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'type'        => 'gradient',
+      'value'       => $fields->fetch_value('name'),
+      'label'       => 'Gradient',
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description' 
+    ]);
+   
+
diff --git a/example/templates/fields/hidden.php b/example/templates/fields/hidden.php new file mode 100644 index 00000000..621edb15 --- /dev/null +++ b/example/templates/fields/hidden.php @@ -0,0 +1,48 @@ +

Example

+ +
+ render_field('hidden', [ + 'label' => 'Hidden field', + 'type' => 'hidden', + 'value' => $fields->fetch_value('hidden'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'attributes' => [ + 'data-foo' => 'bar', + 'class' => 'baz-class' + ] + ]) + ?> +
+ +
+ +
+ +

Value

+ +fetch_value('hidden') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    $fields->render_field('name', [
+      'label'       => 'Hidden field',
+      'type'        => 'hidden',
+      'value'       => $fields->fetch_value('name'),
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description',
+      'attributes'  => [
+        'data-foo'=> 'bar',
+        'class'   => 'baz-class'
+      ]
+    ])
+  
+
diff --git a/example/templates/fields/list.php b/example/templates/fields/list.php new file mode 100644 index 00000000..21c4310e --- /dev/null +++ b/example/templates/fields/list.php @@ -0,0 +1,80 @@ +

Example - Without visibility button

+ +
+ render_field('list', [ + 'label' => 'List field', + 'type' => 'list', + 'value' => $fields->fetch_value('list'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'choices' => [ + 'test1' => 'Test 1', + 'test2' => 'Test 2', + 'test3' => 'Test 3' + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('list') +); ?> + +

Example - With visibility button

+ +
+ render_field('list-with-visibility', [ + 'label' => 'List field', + 'type' => 'list', + 'value' => $fields->fetch_value('list-with-visibility'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'use_visibility' => true, + 'choices' => [ + 'test1' => 'Test 1', + 'test2' => 'Test 2', + 'test3' => 'Test 3' + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('list-with-visibility') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    $fields->render_field('field-name', [
+      'label'       => 'Hidden field',
+      'type'        => 'list',
+      'value'       => $fields->fetch_value('field-name'),
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description',
+
+      // Optional - Default false
+      'use_visibility' => true,
+
+      'choices'     => [
+        'test1' => 'Test 1',
+        'test2' => 'Test 2',
+        'test3' => 'Test 3'
+      ]
+    ])
+  
+
diff --git a/example/templates/fields/number.php b/example/templates/fields/number.php new file mode 100644 index 00000000..490b6509 --- /dev/null +++ b/example/templates/fields/number.php @@ -0,0 +1,56 @@ +

Example

+ +
+ render_field('number', [ + 'label' => 'Number field', + 'type' => 'number', + 'value' => $fields->fetch_value('number'), + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example for a value between 10 and 20

+ +
+ render_field('number_max', [ + 'label' => 'Number field', + 'type' => 'number', + 'value' => $fields->fetch_value('number_max'), + 'min' => 10, + 'max' => 20, + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('number'), + $fields->fetch_value('number_max'), +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Number field',
+      'type'        => 'number',
+      'value'       => $fields->fetch_value('name'),
+      'min'         => 10, // Optional
+      'max'         => 20, // Optional
+      'description' => 'Example description'
+    ]);
+  
+
diff --git a/example/templates/fields/radio.php b/example/templates/fields/radio.php new file mode 100644 index 00000000..b65e1bc4 --- /dev/null +++ b/example/templates/fields/radio.php @@ -0,0 +1,45 @@ +

Example

+ +
+ render_field('radio', [ + 'label' => 'Radio field', + 'type' => 'radio', + 'value' => $fields->fetch_value('radio'), + 'choices' => [ + '1' => 'Value 1', + '2' => 'Value 2' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('radio') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field( 'name', [
+      'label'   => 'Text field',
+      'type'    => 'radio',
+      'choices' => [
+        '1' => 'Value 1',
+        '2' => 'Value 2'
+      ],
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+    ]);
+  
+
diff --git a/example/templates/fields/select.php b/example/templates/fields/select.php new file mode 100644 index 00000000..0e131bab --- /dev/null +++ b/example/templates/fields/select.php @@ -0,0 +1,160 @@ +

Example

+ +
+ render_field('select', [ + 'type' => 'select', + 'value' => $fields->fetch_value('select'), + 'label' => 'Select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with categories

+ +
+ render_field('select_categories', [ + 'type' => 'select', + 'value' => $fields->fetch_value('select_categories'), + 'label' => 'Select', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2'=> 'Test2' + ] + ], + [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ], + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with multiple values

+ +
+ render_field('select_multiple', [ + 'type' => 'select', + 'value' => $fields->fetch_value('select_multiple'), + 'label' => 'Select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + 'multiple' => true, + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example with multiple values and categories

+ +
+ render_field('select_multiple_categories', [ + 'type' => 'select', + 'value' => $fields->fetch_value('select_multiple_categories'), + 'label' => 'Select', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2'=> 'Test2' + ] + ], + [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ], + 'multiple' => true, + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('select'), + $fields->fetch_value('select_categories'), + $fields->fetch_value('select_multiple'), + $fields->fetch_value('select_multiple_categories'), +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'type'    => 'select',
+      'value'   => $fields->fetch_value('name'),
+      'label'   => 'Select',
+      'choices' => [
+
+        // Simple List
+        'test1' => 'Test1',
+        'test2' => 'Test2'
+
+        // If categories
+        [
+          'name'    => 'Category 1',
+          'choices' => [ 
+            'test1' => 'Test1',
+            'test2' => 'Test2'
+          ]
+        ],
+        [
+          'name'    => 'Category 2',
+          'choices' => [ 
+            'test3' => 'Test3',
+            'test4' => 'Test4'
+          ]
+        ]        
+
+      ],
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description',
+
+      // If multiple values allowed
+
+      'multiple'    => true, 
+    ]);
+   
+
diff --git a/example/templates/fields/select3.php b/example/templates/fields/select3.php new file mode 100644 index 00000000..dc7952aa --- /dev/null +++ b/example/templates/fields/select3.php @@ -0,0 +1,70 @@ +

Example Single-select

+ +
+ render_field('select3_single_select', [ + 'type' => 'select3', + 'value' => $fields->fetch_value('select3_single_select'), + 'label' => 'Select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4', + 'test5' => 'Test5', + 'test6' => 'Test6', + 'test7' => 'Test7', + ], + 'placeholder' => 'Select for a choice', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example Multi-select

+ +
+ render_field('select3_multi_select', [ + 'type' => 'select3', + 'value' => $fields->fetch_value('select3_multi_select'), + 'label' => 'Select', + 'multiple' => true, + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', +// [ +// 'name' => 'Category 2', +// 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' +// ] +// ] + ], + 'placeholder' => 'Select for a choice', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +see( + $fields->fetch_value('select3_single_select'), + $fields->fetch_value('select3_multi_select') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', []);
+  
+
diff --git a/example/templates/fields/simple-dimension.php b/example/templates/fields/simple-dimension.php new file mode 100644 index 00000000..7297fbda --- /dev/null +++ b/example/templates/fields/simple-dimension.php @@ -0,0 +1,38 @@ +

Example

+ +
+ render_field('simple_dimension', [ + 'label' => 'Simple Dimension field', + 'type' => 'simple_dimension', + 'description' => 'Description', + 'units' => [ 'px','vh','%','vw' ], + 'value' => $fields->fetch_value('simple_dimension'), + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('simple_dimension') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    $fields->render_field('name', [
+      'label'       => 'Simple Dimension field', // Optional
+      'type'        => 'simple_dimension',
+      'description' => 'Description', // Optional
+      'units'       => [ 'px','vh','%','vw' ], // Optional, default px
+      'value'       => $fields->fetch_value('name'),
+    ]);
+  
+
diff --git a/example/templates/fields/switch.php b/example/templates/fields/switch.php new file mode 100644 index 00000000..97ddd11a --- /dev/null +++ b/example/templates/fields/switch.php @@ -0,0 +1,40 @@ +

Example

+ +
+ render_field('switch', [ + 'label' => 'Switch field', + 'type' => 'switch', + 'description' => 'Description', + 'value_on' => 'on', + 'value_off' => 'off', + 'value' => $fields->fetch_value('switch'), + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('switch') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'       => 'Switch field',
+      'type'        => 'switch',
+      'value'       => $fields->fetch_value('name'),
+      'description' => 'Description',
+      'value_on'    => 'on', // Optional, default on
+      'value_off'   => 'off',  // Optional, default off
+    ]);
+   
+
diff --git a/example/templates/fields/text-suggestion.php b/example/templates/fields/text-suggestion.php new file mode 100644 index 00000000..49d9238f --- /dev/null +++ b/example/templates/fields/text-suggestion.php @@ -0,0 +1,97 @@ +I'm really not sure about how to name this one.
+Can contain both text and dynamic element from the dropdown.
+ +

Example

+ +
+ render_field('dynamic_text', [ + 'label' => 'Test', + 'label' => 'Label', + 'value' => $fields->fetch_value('dynamic_text'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'type' => 'text_suggestion', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4', + ] + ]) ?> +
+ +
+ +
+ +

Example with categories

+ +
+ render_field('dynamic_text_categories', [ + 'label' => 'Test', + 'label' => 'Label', + 'value' => $fields->fetch_value('dynamic_text_categories'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'type' => 'text_suggestion', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ],[ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('dynamic_text'), + $fields->fetch_value('dynamic_text_categories') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    // Option can be with or without categories
+
+    echo $fields->render_field('name', [
+      'label'       => 'Dynamic text',
+      'placeholder' => 'Example placeholder', 
+      'description' => 'Example description',
+      'type'        => 'text_suggestion',
+      'choices'     => [
+        [
+          'name'    => 'Category 1',
+          'choices' => [ 
+            'test1' => 'Test1',
+            'test2' => 'Test2'
+          ]
+        ],[
+          'name'    => 'Category 2',
+          'choices' => [ 
+            'test3' => 'Test3',
+            'test4' => 'Test4'
+          ]
+        ]
+      ]
+    ])
+   
+
diff --git a/example/templates/fields/text.php b/example/templates/fields/text.php new file mode 100644 index 00000000..07510883 --- /dev/null +++ b/example/templates/fields/text.php @@ -0,0 +1,146 @@ +

Example

+ +
+ render_field('text', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('text'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('text') +); ?> + +

Example with readOnly

+ +
+ render_field('text-read-only', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('text-read-only'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'read_only' => true + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('text-read-only') +); ?> + +

Example with inputMask

+ +
+ render_field('text-mask', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('text-mask'), + 'placeholder' => '', + 'description' => 'Mask: "999-aaa"', + 'input_mask' => '999-aaa' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('text-mask') +); ?> + +

Example with prefix and suffix

+ +
+ render_field('text-prefix-suffix', [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => $fields->fetch_value('text-prefix-suffix'), + 'placeholder' => '', + 'description' => 'description', + 'prefix' => 'PRE-', + 'suffix' => '-SUF' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('text-prefix-suffix') +); ?> + +

Example with prefix, suffix, and mask

+ +
+ render_field('text-prefix-suffix-mask', [ + 'label' => 'Text field', + 'type' => 'text', +// 'value' => $fields->fetch_value('text-prefix-suffix-mask'), +// 'value' => 'PRE-ACCREDAAAA-???BZ-00POST-ACCRED', + 'value' => "PRE-ACCREDAAAA-???BZ-00POST-ACCRED", + 'placeholder' => '', + 'description' => 'description', + 'prefix' => 'PRE-ACCRED', + 'suffix' => 'POST-ACCRED', + 'input_mask' => 'AAAA-???BZ-00' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('text-prefix-suffix-mask') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field( 'name', [
+      'label'       => 'Text field',
+      'type'        => 'text',
+      'value'       => $fields->fetch_value('name'),
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+      'read-only' => true,
+      'prefix' => 'PRE',
+      'suffix' => 'SUF',
+      /**
+       * Instructions:
+       * - '9' = numerical character.
+       * - 'a' = alphabetical character.
+       * - '*' = any alphanumeric character.
+       * - All other characters are literal values and will be displayed automatically.
+      */
+      'input_mask'      => 'aaa-99'
+    ]);
+  
+
diff --git a/example/templates/fields/textarea.php b/example/templates/fields/textarea.php new file mode 100644 index 00000000..73b5f4dd --- /dev/null +++ b/example/templates/fields/textarea.php @@ -0,0 +1,57 @@ +

Example

+ +
+ render_field('textarea', [ + 'label' => 'Text Area', + 'type' => 'textarea', + 'value' => $fields->fetch_value('textarea'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'maxlength' => 150, + 'minlength' => 10, + 'required' => false, + 'rows' => 8, + 'wrapper' => [ + 'data-foo' => 'bar', + 'class' => 'foo-class' + ] + ] ) + ?> +
+ +
+ +
+ +

Value

+ +fetch_value('textarea') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field( 'name', [
+      'label'       => 'Text Area',
+      'type'        => 'textarea',
+      'value'       => $plugin->get_settings()['setting_textarea_name'] ?? '',
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description',
+      'maxlength'   => 150,
+      'minlength'   => 10,
+      'required'    => false,
+      'rows'        => 8,
+      'wrapper'     => [
+        'data-foo' => 'bar',
+        'class'    => 'foo-class'
+      ]
+    ] )
+  
+
+ diff --git a/example/templates/fields/wysiwyg.php b/example/templates/fields/wysiwyg.php new file mode 100644 index 00000000..d6bfd7d1 --- /dev/null +++ b/example/templates/fields/wysiwyg.php @@ -0,0 +1,83 @@ +We support two options for the editor: ProseMirror and TinyMCE. + +
    +
  • ProseMirror: default editor
  • +
  • TinyMce: to use TinyMce editor, set the parameter 'editor' to 'editor' => 'tinymce'
+ + +

Example - ProseMirror

+ +
+ render_field('wysiwyg', [ + 'label' => 'Editor field', + 'type' => 'wysiwyg', + 'value' => $fields->fetch_value('wysiwyg'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Example - ProseMirror without raw_view

+ +
+ render_field('wysiwyg-visual', [ + 'label' => 'Editor field', + 'type' => 'wysiwyg', + 'value' => $fields->fetch_value('wysiwyg-visual'), + 'placeholder' => 'Example placeholder', + 'description' => 'Example description', + 'raw_view' => false + ]) ?> +
+ +
+ +
+ +

Example - TinyMce

+ +
+ render_field('wysiwyg-tinymce', [ + 'label' => 'Editor field', + 'type' => 'wysiwyg', + 'value' => $fields->fetch_value('wysiwyg-tinymce'), + 'editor' => 'tinymce', + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ]) ?> +
+ +
+ +
+ +

Value

+ +fetch_value('wysiwyg'), + $fields->fetch_value('wysiwyg-visual'), + $fields->fetch_value('wysiwyg-tinymce') +); ?> + +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field( 'name', [
+      'label'       => 'Editor field',
+      'type'        => 'wysiwyg',
+      'value'       => $fields->fetch_value('name'),
+      'editor'      => 'tinymce',
+      'raw_view'    => false, // Only with prosemirror editor, default true
+      'placeholder' => 'Example placeholder',
+      'description' => 'Example description'
+    ]);
+  
+
diff --git a/example/templates/installation/composer.md b/example/templates/installation/composer.md new file mode 100644 index 00000000..3034f2f2 --- /dev/null +++ b/example/templates/installation/composer.md @@ -0,0 +1,37 @@ +#### Use in a plugin + +To use the module inside the plugin, you need to update your composer.json. It's required to add the framework in the repositories list as well even if your project dosen't use it directly, because it's used by the fields module and [repositories needs to be defined at the root](https://getcomposer.org/doc/04-schema.md#repositories): +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:tangibleinc/fields.git" + }, + { + "type": "vcs", + "url": "git@github.com:tangibleinc/framework.git" + } + ], + "require": { + "tangible/fields": "dev-main" + }, + "minimum-stability": "dev" +} +``` + +The module can then be installed in your project using: +``` +composer install +``` + +The module needs to be manually required in your code: +```php +require_once __DIR__ . '/vendor/tangible/fields/index.php'; +``` + +You can then access the module from anywhere using this function: +```php +$fields = tangible_fields(); +``` + diff --git a/example/templates/introduction/introduction.md b/example/templates/introduction/introduction.md new file mode 100644 index 00000000..96974d77 --- /dev/null +++ b/example/templates/introduction/introduction.md @@ -0,0 +1,46 @@ +#### Concept + +Tangible Fields is a composer module designed to easily generate form fields that utilize a unified styling across multiple code bases. This module can be used from both PHP and JavaScript (although this documentation only covers the PHP part at the moment). + +It's important to note that currently, this module is purely front-end focused and does not handle getting or saving data by itself. + +#### How it Works + +To generate a field, we pass a field name and a configuration array to the `render_field()` method, which return an empty `
` element with a unique ID: + +```php +$fields = tangible_fields(); + +$field_html = $fields->render_field('field_name', + [ + 'label' => 'Text field', + 'type' => 'text', + 'value' => '', + 'placeholder' => 'Example placeholder', + 'description' => 'Example description' + ] +); +``` + +In this example, the value of `$field_html` will be something like this: +```html +
+``` + +The module keeps track of which field name/configuration is associated to which `') ?>` thanks to the id and passes all data (div id + field configuration) to the frontend. The appropriate React component is then initialized inside the correct `
` based on this. + +Each field type is linked to a react component. You can see which field type is associated with which component in the [types.js file](https://bitbucket.org/tangibleinc/tangible-fields-module/src/main/assets/src/types.js). + +When a field is initialized, the associated config is passed to the component as react props. + +To generate the JS/CSS build file, we use an internal tool called Tangible Roller (the documentation can be found [here](https://develop.tangible.one/tools/roller)). + +#### Getting started + +An easy way to work on the Tangible Fields module is to work from the [documentation repository](https://bitbucket.org/tangibleinc/tangible-fields-example/src/main/), which is a wordpress plugin that uses the Tangible Fields as a composer dependency. + +One advantage of working from this plugin is that all the possible fields are already included in the documentation as example, an can be used for testing. + +As long as Tangible Fields is used as a composer module, so the repository will be available in /vendor/tangible/fields. You can work in this folder directy (it should already be a git repository if the dependencies has been installed by running `composer install`). + +The module can also be used in any plugin as a composer module by following the documenation on composer installation page. diff --git a/example/templates/javascript/events.md b/example/templates/javascript/events.md new file mode 100644 index 00000000..9a52f9b3 --- /dev/null +++ b/example/templates/javascript/events.md @@ -0,0 +1,92 @@ +Tangible Fields include an event/trigger system that you can use from the `tangibleFields` object. + +#### Summary + +- [Listen to en event](#listen) +- [Stop listening to an event](#remove) +- [Trigger an event](#trigger) +- [Event list](#events) + - [initField](#init-field) + - [valueChange](#value-change) + - [buttonPressed](#button-pressed) + +#### Listen to en event {#listen} + +To listen to a fields event, you can use the `event` method from `tangibleFields`. + +It take two arguments: + +- `eventName`: The name of the event +- `eventCallback`: A callback function that will be executed when the specified event is triggered. It receives two parameters: + - `eventData`: An object containing any additional data or arguments passed when triggering the event + - `event`: The event object itself + +```javascript +// The callback will be called each time any value change +tangibleFields.event('valueChange', (field, event) => { + + if( field.name !== 'field-name' ) return; + + console.log('field-name value is now ' + field.value) +}) +``` + +#### Stop listening to an event {#remove} + +The event method returns a function that can be used to remove the event listener when it's no longer needed: + +```javascript +const deleteEvent = tangibleFields.event('eventName', callback) + +// Stop listening after 10 seconds +setTimeout(() => { + deleteEvent() +}, 10000) +``` + +#### Trigger an event {#trigger} + +To trigger a fields event, you can use the `trigger` method from `tangibleFields`. + +It take two arguments: +- `eventName`: The name of the event +- `args`: (optional) Arguments to pass to the triggered event + +```javascript +tangibleFields.trigger('myCustomEvent', args) +``` + +#### Event list {#events} + + - ##### initField {#init-field} + Triggered when a field is initialized: + ```javascript + tangibleFields.event('initField', field => { + field.name // Field name + field.props // Configuration passed when creating the field (type, label ...etc) + }) + ``` + + - ##### valueChange {#value-change} + Triggered each time a field value change. Is also triggered for subfields from repeaters and field-groups: + ```javascript + tangibleFields.event('valueChange', field => { + + field.name // Field name, or false if no name (it will be the case for subfields) + field.props // Configuration passed when creating the field (type, label ...etc) + field.value // New field value + + // Note: You can check if the change happened in a subfield with this condition: + const isSubfield = field.props?.controlType === 'subfield' + }) + ``` + + - ##### buttonPressed {#button-pressed} + Triggered each time a button is pressed: + ```javascript + tangibleFields.event('buttonPressed', button => { + button.name // Or false if no name + button.props // Configuration passed when creating the button + button.event + }) + ``` diff --git a/example/templates/javascript/fields.md b/example/templates/javascript/fields.md new file mode 100644 index 00000000..c42744d3 --- /dev/null +++ b/example/templates/javascript/fields.md @@ -0,0 +1,6 @@ +#### Re-render + +It's possible to force a full re-render of a given field from JavaScript with the following code: +```javascript +tangibleFields.fields.rerender('field-name') +``` diff --git a/example/templates/javascript/render.md b/example/templates/javascript/render.md new file mode 100644 index 00000000..e087c742 --- /dev/null +++ b/example/templates/javascript/render.md @@ -0,0 +1,92 @@ + +#### Summary + +- [Render](#render) +- [onChange event](#on-change) +- [Style context](#style-context) +- [Attribute names](#attribute-names) + +#### Render method {#render} + +To interact with fields from the JavaScript side, we rely on the `tangibleFields` object, which is available globally when fields is enqueue. + +To render fields from the JavaScript directly, we can use the `render` method of the `tangibleFields` object: + +```javascript +tangibleFields.render({ + type : 'text', + label : 'Text' + name : 'field-name' +}) +``` + +As the returned value of `render` is React component, it has to be rendered inside a React app or inside an existing React component. + +Example inside a react app: +```javascript +import { createRoot } from 'react-dom' + +const component = tangibleFields.render({ + type : 'text', + label : 'Text' + name : 'field-name' +}) + +const container = document.getElementById('container-id') +createRoot(container).render(component) +``` + +#### onChange event {#on-change} + +It's possible to add an `onChange` callback on a created field and get the new value each time it's updated: +```javascript +tangibleFields.render({ + ...attributes, + onChange: newValue => { + console.log('Field value is now ' + newValue) + } +}) +``` + +#### Style context {#style-context} + +_Note: This feature exists in PHP as well but is currently undocumented._ + +You can chose the style context of the curent field by setting a `context` prop, however you will have to make sure that the associated stylesheet has been enqueued on the page: +```javascript +tangibleFields.render({ + ...attributes, + context: 'wp' +}) +``` + +#### Attribute names {#attribute-names} + +Every field type is supported, and will support the same arguments that fields rendered from PHP. + +The only difference is that in PHP attributes will use `snake_case`, while in JS it will use `camelCase`. + +Example of a color field rendered from PHP +```php +$fields = tangible_fields(); +$color_picker = $fields->render_field('color', [ + 'type' => 'color_picker', + 'enable_opacity' => true + 'value' => $value, + 'label' => 'Color' +]); +``` + +Same field rendered in JS +```javascript +const colorPicker = tangibleFields.render({ + name : 'color', + type : 'colorPicker', + enableOpacity : true, + label : 'Color' +}) +``` + + + + diff --git a/example/templates/javascript/store.md b/example/templates/javascript/store.md new file mode 100644 index 00000000..d00e2961 --- /dev/null +++ b/example/templates/javascript/store.md @@ -0,0 +1,89 @@ +The store object is designed to manage and store field values. It provides methods to interact with the stored data. + +To use it, you can access it via the `tangibleFields.store` property. Here's how to use it: + +```javascript +const store = tangibleFields.store +``` + +#### Summary + +- [getAllValues()](#getAllValues) +- [getValue()](#getValue) +- [setValue()](#setValue) +- [getRepeater()](#getRepeater) + - [getRow](#getRow) + - [setRow](#setRow) + - [getRowValue](#getRowValue) + - [setRowValue](#setRowValue) + +#### getAllValues {#getAllValues} + +If you need to retrieve all values stored in the store, you can use the `getAllValues` method: + +```javascript +const store = tangibleFields.store +const allValues = store.getAllValues() +``` + +This method returns an object containing all the key-value pairs stored in the `store._values` object. + +#### getValue {#getValue} + +To retrieve the value of a specific field, you can use the getValue method: + +```javascript +const store = tangibleFields.store +const storedValue = store.getValue('field-name') +``` + +The `getValue` method takes a name as an argument and returns the corresponding value stored in the store. If the value does not exist, it returns an empty string by default. + +#### setValue {#setValue} + +You can change the value of an existing field using the `setValue` method: + +```javascript +const store = tangibleFields.store +const storedValue = store.setValue('field-name', 'new-value') +``` + +#### getRepeater {#getRepeater} + +If you need to work with repeater controls, you can use the `getRepeater` method to obtain a repeater store object: + +```javascript +const store = tangibleFields.store +const repeater = store.getRepeater('repeater-name') +``` + +The `repeater` object obtained from `getRepeater` allows you to interact with repeater data using methods like `getRow`, `setRow`, `getRowValue`, and `setRowValue`. + +_Note: In the current implementation, changing the value of a given row/field will trigger a complete re-render of the asscoiated repeater. It can be optimized in the future if needed to only re-render the associated row/field._ + + - ##### getRow {#getRow} + ```javascript + const rowNumber = 0 + const row = repeater.getRow(rowNumber) + ``` + + - ##### setRow {#setRow} + ```javascript + const rowNumber = 0 + repeater.setRow(rowNumber, {, + 'repeater-field-1' : 'value-1', + 'repeater-field-2' : 'value-2' + }) + ``` + + - ##### getRowValue {#getRowValue} + ```javascript + const rowNumber = 0 + const value = repeater.getRowValue(rowNumber, 'field-name') + ``` + + - ##### setRowValue {#setRowValue} + ```javascript + const rowNumber = 0 + repeater.setRowValue(rowNumber, 'field-name', 'new-value') + ``` diff --git a/example/templates/javascript/types.php b/example/templates/javascript/types.php new file mode 100644 index 00000000..a617d39e --- /dev/null +++ b/example/templates/javascript/types.php @@ -0,0 +1,106 @@ +

Register a new field type

+ +

It's possible to register your own field type from your script, by registering a React component and associating it with a new field type.

+

It can be done with the tangibleFields.types.add() function:

+ +start_code('javascript') ?> +tangibleFields.types.add('custom-field-type', CustomFieldComponent) +end_code() ?> + +

Create your custom field component

+ +In order to interact correctly with fields, your custom component must: +
    +
  • Get its initial value from props.value
  • +
  • If props.onChange is defined, return the new value each time it changes
  • +
  • When props.name is defined, make the value accessible from a traditional html form (by defining the value in an hidden input for example)
  • +
+ +

Here is a minimal example:

+start_code('javascript') ?> +const CustomFieldComponent = props => { + + const [value, setValue] = useState(props.value ?? '') + + // ... + + useEffect(() => props.onChange ? props.onChange(value) : null, [value]) + + // ... + + return( + <> + + // ... + + ) +} +end_code() ?> + +

Example - User info field type

+ +Here is a working example with a JSON value: +
+ +
+ +
+ fetch_value('custom-field-example') + ); ?> +
+ +
+
+ +
+ start_code('javascript') ?> +const UserForm = props => { + + const { initJSON } = tangibleFields.utils + const [value, setValue] = useState( initJSON(props.value ?? {}) ) + + useEffect(() => { + if( props.onChange ) props.onChange(value) + }, [value]) + + return( +
+ +
+ Character attributes: +
+ { tangibleFields.render({ + label : 'First name', + type :'text', + value : value.firstName ?? '', + onChange : firstName => setValue({ ...value, firstName: firstName }), + }) } + { tangibleFields.render({ + label : 'Last name', + type :'text', + value : value.lastName, + onChange : lastName => setValue({ ...value, lastName: lastName }), + }) } +
+ { tangibleFields.render({ + label : 'Avatar', + type : 'file', + maxUpload : 1, + wp_media : false, + value : value.avatar ?? '', + onChange : avatar => setValue({ ...value, avatar: avatar }), + mimeTypes : [ 'image' ] + }) } +
+
+ ) +} +tangibleFields.types.add('user-form', UserForm) + end_code() ?> +
+
diff --git a/example/templates/registration/save-load.md b/example/templates/registration/save-load.md new file mode 100644 index 00000000..8d5e46fd --- /dev/null +++ b/example/templates/registration/save-load.md @@ -0,0 +1,222 @@ +#### Summary + +- [Save/load configuration](#save-load) +- [Fetch and store callbacks](#store-and-fetch) + - [Register callbacks](#register-store-and-fetch) + - [Helpers](#store-and-fetch-helpers) +- [Permissions callbacks](#permissions) + - [Register callbacks](#register-permissions) + - [Helpers](#permissions-helpers) +- [Validation callbacks](#validation) +- [Full registration example](#full-registration-example) + +#### Save/load configuration {#save-load} + +Using the save/load feature will allow you to easily access and update field value by using the following functions: +```php +$value = $fields->fetch_value('field-name'); +``` +```php +$fields->store_value('field-name', $updated_value); +``` + +In order to use `$fields->fetch_value()` and `$fields->store_value()`, the following callbacks needs to be set during the field registration: +- [Store and fetch callbacks](#store-and-fetch) +- [Permissions callbacks](#permissions) +- [Validation callbacks](#validation) + +#### Store and fetch callbacks {#store-and-fetch} + +##### Register callbacks {#register-store-and-fetch} + +To determine how data are stored, we need to set 2 parameters: +- `store_callback` - Function used to update field value. It takes the `$field_name` and the `$field_value` as parameters. It returns `true` if the value is successfully updated and `false` otherwise. +- `fetch_callback` - Function that takes the `$field_name` as a parameter. + +Here is an example of callbacks that will store a field value as a cookie: +```php +$fields->register_field('field-name', [ + // .... + 'store_callback' => function($name, $value) { + return setcookie(`tf-{$name}`, $value); + }, + 'fetch_callback' => function() { + return $_COOKIE[`tf-{$name}`] ?? ''; + }, + // .... +]); +``` + +##### Helpers {#store-and-fetch-helpers} + +Some helpers are available for common cases: +- Field stored in the `wp_options` table +- Field stored in the `wp_{object}meta` table (works for `post`, `comment`, `term` and `user` metas) +- Field stored in the php `memory` + +Here is an example of how to use each of them: +```php + +/** + * Stored in `wp_options` table: + * - $prefix: optional - if no set 'tf-' will be used + */ +$fields->register_field('field-name', [ + // .... + ...$fields->_store_callbacks['options']('prefix_'), + // .... +]); + +/** + * Stored in `wp_{object}meta` table: + * - $object_type: `post`, `comment`, `term` or `user` + * - $object_id + * - $prefix: optional - if no set 'tf-' will be used + */ +$fields->register_field('field-name', [ + // .... + ...$fields->_store_callbacks['meta']('post', $post_id, 'prefix_'), + // .... +]); + +/** + * Stored in php memory: + */ +$fields->register_field('field-name', [ + // .... + ...$fields->_store_callbacks['memory'](), + // .... +]); +``` + +#### Permissions callbacks {#permissions} + +##### Register callbacks {#register-permissions} + +To determine if the current user is allowed to store or fetch the field's value, we need to set 2 parameters: +- `permission_callback_store` - Function that returns `true` if user is allowed to update the field value, `false` otherwise. Takes `$fields_name` as a parameter. +- `permission_callback_fetch` - Function that returns `true` if user is allowed to read the field value, `false` otherwise. Takes `$fields_name` as a parameter. + +If the both read and write require the same permission, you can use a single callback called `permission_callback` instead. + +_Note: If the permission callbacks are not defined, fields will always assume that the current user is not allowed to update or read the field value._ + +Here is an example of how to define permissions: +```php + +/** + * Different permissions for read and write + */ +$fields->register_field('field-name', [ + // .... + 'permission_callback_store' => function($field_name) { + return is_user_logged_in(); + }, + 'permission_callback_fetch' => function($field_name) { + return true; + }, + // .... +]); + +/** + * Same permissions for read and write + */ +$fields->register_field('field-name', [ + // .... + 'permission_callback' => function($field_name) { + return is_user_logged_in(); + }, + // .... +]); +``` + +##### Helpers {#permissions-helpers} + +Some helpers are available for common cases: + +- Always allows +- Allows if user has one or multiple [wordpress capabilities](https://wordpress.org/documentation/article/roles-and-capabilities/#capabilities) + +Here is an example of how to use both: +```php + +/** + * Permissions according to capabilities - Only if user has manage_options capability + */ +$fields->register_field('field-name', [ + // .... + $fields->_permission_callbacks([ + 'store' => ['user_can', 'manage_options'] + 'fetch' => ['user_can', 'manage_options'] + ]) + // .... +]); + +/** + * Always allows + */ +$fields->register_field('field-name', [ + // .... + $fields->_permission_callbacks([ + 'store' => ['always_allow'] + 'fetch' => ['always_allow'] + ]) + // .... +]); +``` + +#### Validation callbacks {#validation} + +Validation callbacks are called before a value is saved. + +A field can have multiple validation callbacks, and each one must return true in order to save the value. + +Here is an example with a number field that must both be an integer and be less than 5: +```php +$fields->register_field('field-name', [ + // .... + 'validation_callbacks' = [ + function($name, $value) { + return is_int($value); + }, + function($name, $value) { + return $value < 5; + } + ] + // .... +]); +``` + +#### Full registration example {#full-registration-example} + +```php +/** +* @see https://bitbucket.org/tangibleinc/tangible-fields-module/src/main/store.php +*/ +$fields->register_field('field-name', [ + ...$args, + 'store_callback' => function($name, $value) { + return setcookie(`tf-{$name}`, $value); + }, + 'fetch_callback' => function() { + return $_COOKIE[`tf-{$name}`] ?? ''; + }, + // Permissions + 'permission_callback_store' => function($field_name) { + return is_user_logged_in(); + }, + 'permission_callback_fetch' => function($field_name) { + return true; + }, + // Validation + 'validation_callbacks' = [ + function($name, $value) { + return is_int($value); + }, + function($name, $value) { + return $value < 5; + } + ] +]); +``` + diff --git a/example/templates/registration/usage.md b/example/templates/registration/usage.md new file mode 100644 index 00000000..c90a513c --- /dev/null +++ b/example/templates/registration/usage.md @@ -0,0 +1,24 @@ +#### Description + +Registration is required if you render your field from `php`. + +To register a field, the `$fields->register_field()` function is used. It takes 2 arguments: +- The field name +- An array with the configuration of the field that can contain: + - The configuration needed for the save/load feature (it has to be set if you want to rely on the fields built-in save/load feature) + - The configuration needed for field render (field type, label etc). If the render configuration is defined in the registration, you don't need to set it again when calling `$fields->render_field()` + +```php +$fields = tangible_fields(); + +$fields->register_field('field-name', [ + 'type' => 'text', + 'label' => 'My text field' +]); + +echo $fields->render_field('field-name'); +``` + +More information on the save/load configuration in the next page. + + diff --git a/example/templates/repeater/advanced.php b/example/templates/repeater/advanced.php new file mode 100644 index 00000000..85e319d8 --- /dev/null +++ b/example/templates/repeater/advanced.php @@ -0,0 +1,86 @@ +A repeater row can contain any fields. + +

Example repeater with advanced layout

+ +
+ render_field('repeater_advanced', [ + 'label' => 'Repeater field', + 'type' => 'repeater', + 'layout' => 'advanced', + 'value' => $fields->fetch_value('repeater_advanced'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'date_picker', + 'name' => 'date', + ], + [ + 'label' => 'Operator', + 'type' => 'select', + 'name' => 'select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + ], + [ + 'label' => 'Color', + 'type' => 'color_picker', + 'name' => 'color', + ], + ] + ]); ?> +
+ +
+ +
+ +
fetch_value('repeater_advanced')) ?>
+ +

Code

+ +start_code('php') ?> +$fields->render_field('repeater_bare', [ + 'label' => 'Repeater field', + 'type' => 'repeater', + 'layout' => 'bare', + 'value' => $fields->fetch_value('repeater_advanced'), + + /** + * Optional: + * By default all fields will be displayed in the overview row, but it's possible + * to only set a few if needed with the header_fields parameter + */ + 'header_fields' => [ + 'date_picker', + 'color_picker' + ], + + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'date_picker', + 'name' => 'date', + ], + [ + 'label' => 'Operator', + 'type' => 'select', + 'name' => 'select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + ], + [ + 'label' => 'Color', + 'type' => 'color_picker', + 'name' => 'color', + ], + ] +]); +end_code() ?> diff --git a/example/templates/repeater/bare.php b/example/templates/repeater/bare.php new file mode 100644 index 00000000..35d844c9 --- /dev/null +++ b/example/templates/repeater/bare.php @@ -0,0 +1,81 @@ +A repeater row can contain any fields. + +

Example repeater with bare layout

+ +
+ render_field('repeater_bare', [ + 'label' => 'Repeater field', + 'type' => 'repeater', + 'layout' => 'bare', + 'value' => $fields->fetch_value('repeater_bare'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'date_picker', + 'name' => 'date', + 'label_visually_hidden' => true + ], + [ + 'label' => 'Operator', + 'type' => 'select', + 'name' => 'select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + 'label_visually_hidden' => true, + ], + [ + 'label' => 'Color', + 'type' => 'color_picker', + 'name' => 'color', + 'label_visually_hidden' => true, + ], + ] + ]); ?> +
+ +
+ +
+ +
fetch_value('repeater_bare')) ?>
+ +

Code

+ +start_code('php') ?> +$fields->render_field('repeater_bare', [ + 'label' => 'Repeater field', + 'type' => 'repeater', + 'layout' => 'bare', + 'value' => $fields->fetch_value('repeater_bare'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'date_picker', + 'name' => 'date', + 'label_visually_hidden' => true + ], + [ + 'label' => 'Operator', + 'type' => 'select', + 'name' => 'select', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2', + 'test3' => 'Test3', + 'test4' => 'Test4' + ], + 'label_visually_hidden' => true, + ], + [ + 'label' => 'Color', + 'type' => 'color_picker', + 'name' => 'color', + 'label_visually_hidden' => true, + ], + ] +]); +end_code() ?> diff --git a/example/templates/repeater/block.php b/example/templates/repeater/block.php new file mode 100644 index 00000000..40647bb1 --- /dev/null +++ b/example/templates/repeater/block.php @@ -0,0 +1,251 @@ +A repeater row can contain any field. + +

Example with Base Repeater Block

+ +
+ render_field('repeater_block', [ + 'label' => 'Repeater field', + 'type' => 'repeater', + 'layout' => 'block', + 'value' => $fields->fetch_value('repeater_block'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]); ?> +
+ +
+ +
+ +

Example with `'repeatable' => false` property

+ +
+ render_field('repeater_block_non_repeatable', [ + 'label' => 'Repeater Repeatable Field', + 'type' => 'repeater', + 'layout' => 'block', + 'repeatable'=> false, + 'value' => $fields->fetch_value('repeater_block_non_repeatable'), + 'sub_fields'=> [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Example with `'maxlength' => 3` property

+ +
+ render_field('repeater_block_max', [ + 'label' => 'Repeater Maxlength Field', + 'type' => 'repeater', + 'layout' => 'block', + 'maxlength' => 3, + 'value' => $fields->fetch_value('repeater_block_max'), + 'sub_fields'=> [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Example with `'use_bulk' => true` , `'use_switch' => true` and `'section_title' => 'My section title'` properties

+ +
+ render_field('repeater_block_bulk_switch_title', [ + 'label' => 'Repeater Bulk Switch Custom Title Field', + 'type' => 'repeater', + 'layout' => 'block', + 'use_bulk' => true, + 'use_switch' => true, + 'section_title' => 'My section title', + 'value' => $fields->fetch_value('repeater_block_bulk_switch_title'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +
fetch_value('repeater_block')) ?>
+
fetch_value('repeater_block_non_repeatable')) ?>
+
fetch_value('repeater_block_max')) ?>
+
fetch_value('repeater_block_bulk_switch_title')) ?>
+ +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'   => 'Repeater field',
+      'type'    => 'repeater',
+      'layout'  => 'block',
+      'value'   => $fields->fetch_value('name'),
+
+      // repeatable has higher priority than maxlength
+      'repeatable' => false, // If false, Disallows adding and removing of a single item
+      'maxlength' => 3, // Limits the number of items that can be added on the repeater
+
+      // You can use the 'use_switch' property even without 'use_bulk'. And if you use the 'use_bulk' property without 'use_switch', you'll only have the 'Delete' option.
+      'use_bulk'      => true,
+      'use_switch'    => true,
+      'section_title' => 'My section title',
+
+      // Can be any type of field, just make sure to add a name
+
+      'sub_fields' => [          
+        [
+          'label'   => 'Text',
+          'type'    => 'text',
+          'name'    => 'text_name',
+        ],[
+          'label'   => 'Dynamic text name',
+          'type'    => 'text-suggestion',
+          'name'    => 'dynamic_text_name',
+          'choices' => [
+            [
+              'name'    => 'Category 1',
+              'choices' => [ 
+                'test1' => 'Test1',
+                'test2' => 'Test2'
+              ]
+            ],[
+              'name'    => 'Category 2',
+              'choices' => [ 
+                'test3' => 'Test3',
+                'test4' => 'Test4'
+              ]
+            ]
+          ]
+        ],[
+          'label' => 'Date field',
+          'type'  => 'date_picker',
+          'name'  => 'date_name',
+        ]
+      ]
+    ]); 
+   
+
diff --git a/example/templates/repeater/table.php b/example/templates/repeater/table.php new file mode 100644 index 00000000..637bb99e --- /dev/null +++ b/example/templates/repeater/table.php @@ -0,0 +1,197 @@ +A repeater row can contain any field. + +

Example with Base Repeater Table

+ +
+ render_field('repeater_table', [ + 'label' => 'Repeater Table', + 'type' => 'repeater', + 'layout' => 'table', + 'value' => $fields->fetch_value('repeater_table'), + 'sub_fields' => [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ],[ + 'label' => 'Dynamic text name', + 'type' => 'text-suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ],[ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ],[ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]); ?> +
+ +
+ +
+ +

Example with `'repeatable' => false` property

+ +
+ render_field('repeater_table_non_repeatable', [ + 'label' => 'Repeater Repeatable Field', + 'type' => 'repeater', + 'layout' => 'table', + 'repeatable'=> false, + 'value' => $fields->fetch_value('repeater_table_non_repeatable'), + 'sub_fields'=> [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Example with `'maxlength' => 3` property

+ +
+ render_field('repeater_table_max', [ + 'label' => 'Repeater Maxlength Field', + 'type' => 'repeater', + 'layout' => 'table', + 'maxlength' => 3, + 'value' => $fields->fetch_value('repeater_table_max'), + 'sub_fields'=> [ + [ + 'label' => 'Text', + 'type' => 'text', + 'name' => 'text_name', + ], [ + 'label' => 'Dynamic text name', + 'type' => 'text_suggestion', + 'name' => 'dynamic_text_name', + 'choices' => [ + [ + 'name' => 'Category 1', + 'choices' => [ + 'test1' => 'Test1', + 'test2' => 'Test2' + ] + ], [ + 'name' => 'Category 2', + 'choices' => [ + 'test3' => 'Test3', + 'test4' => 'Test4' + ] + ] + ] + ], [ + 'label' => 'Date field', + 'type' => 'date_picker', + 'name' => 'date_picker_name', + ] + ] + ]) ?> +
+ +
+ +
+ +

Value

+ +
fetch_value('repeater_table')) ?>
+
fetch_value('repeater_table_non_repeatable')) ?>
+
fetch_value('repeater_table_max')) ?>
+ +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('name', [
+      'label'   => 'Repeater field',
+      'type'    => 'repeater',
+      'layout'  => 'table',
+      'value'   => $fields->fetch_value('name'),
+
+      // repeatable has higher priority than maxlength
+      'repeatable' => false, // If false, Disallows adding and removing of a single item
+      'maxlength' => 3, // Limits the number of items that can be added on the repeater
+
+      // Can be any type of field, just make sure to add a name
+
+      'sub_fields' => [          
+        [
+          'label'   => 'Text',
+          'type'    => 'text',
+          'name'    => 'text_name',
+        ],[
+          'label'   => 'Dynamic text name',
+          'type'    => 'text-suggestion',
+          'name'    => 'dynamic_text_name',
+          'choices' => [
+            [
+              'name'    => 'Category 1',
+              'choices' => [ 
+                'test1' => 'Test1',
+                'test2' => 'Test2'
+              ]
+            ],[
+              'name'    => 'Category 2',
+              'choices' => [ 
+                'test3' => 'Test3',
+                'test4' => 'Test4'
+              ]
+            ]
+          ]
+        ],[
+          'label' => 'Date field',
+          'type'  => 'date_picker',
+          'name'  => 'date_name',
+        ]
+      ]
+    ]); 
+   
+
diff --git a/example/templates/visibility/example.php b/example/templates/visibility/example.php new file mode 100644 index 00000000..9fc9d9b1 --- /dev/null +++ b/example/templates/visibility/example.php @@ -0,0 +1,116 @@ +
+ render_field('visibility-text', [ + 'type' => 'text', + 'label' => 'Text input', + 'description' => 'Type "show repeater" to display the repeater', + 'value' => $fields->fetch_value('visibility-text'), + ]); ?> +
+ +
+ render_field('visibility-repeater', [ + 'type' => 'repeater', + 'layout' => 'table', + 'value' => $fields->fetch_value('visibility-repeater'), + 'condition' => [ + 'action' => 'show', + 'condition' => [ + 'visibility-text' => [ '_eq' => 'show repeater' ], + ], + ], + 'sub_fields' => [ + [ + 'name' => 'number-subfield', + 'type' => 'number', + 'label' => 'Row 1', + ], + [ + 'type' => 'text', + 'name' => 'text-subfield', + 'label' => 'If row1 >= 10', + 'condition' => [ + 'action' => 'show', + 'condition' => [ + 'number-subfield' => [ '_gte' => 10 ], + ], + ] + ], + [ + 'name' => 'subfield-3', + 'type' => 'text', + 'label' => 'If row1 < 10 OR row2 contains "third"', + 'condition' => [ + 'action' => 'show', + 'condition' => [ + '_or' => [ + [ 'number-subfield' => [ '_lt' => 10 ] ], + [ 'text-subfield' => [ '_contains' => 'third' ] ], + ] + ], + ], + ], + ] + ]); ?> +
+ +
+ +
+ +

Code

+ +
+  
+    $fields = tangible_fields();
+    render_registation_message(); ?>
+
+    echo $fields->render_field('visibility-text', [
+      'type'        => 'text',
+      'description' => 'Type "show repeater" to display the repeater',
+      'value'       => $fields->fetch_value('visibility-text'),
+    ]);
+
+    echo $fields->render_field('visibility-repeater', [
+      'type'      => 'repeater',
+      'layout'    => 'table',
+      'value'     => $fields->fetch_value('visibility-repeater'),
+      'condition' => [
+        'action'    => 'show',
+        'condition' => [
+          'visibility-text' => [ '_eq' => 'show repeater' ],
+        ],
+      ],
+      'sub_fields' => [
+        [
+          'name' => 'number-subfield',
+          'type' => 'number',
+        ],
+        [
+          'type'      => 'text',
+          'name'      => 'text-subfield',
+          'label'     => 'If row1 >= 10',
+          'condition' => [
+            'action'    => 'show',
+            'condition' => [
+              'number-subfield' => [ '_gte' => 10 ],
+            ],
+          ]
+        ],
+        [
+          'name'      => 'subfield-3',
+          'type'      => 'text',
+          'label'     => 'If row1 < 10 OR row2 contains "third"',
+          'condition' => [
+            'action'    => 'show',
+            'condition' => [
+              '_or' => [
+                [ 'number-subfield' => [ '_lt' => 10 ] ],
+                [ 'text-subfield' => [ '_contains' => 'third' ] ],
+              ]
+            ],
+          ],
+        ],
+      ]
+    ]);
+  
+
diff --git a/example/templates/visibility/operators.php b/example/templates/visibility/operators.php new file mode 100644 index 00000000..33a4cc67 --- /dev/null +++ b/example/templates/visibility/operators.php @@ -0,0 +1,95 @@ +

Is equal

+ +
+  
+      'field-name' => [ '_eq' => 'must be equal to this text' ],
+  
+
+ +

Isn't equal

+ +
+  
+      'field-name' => [ '_neq' => 'must not be equal to this text' ],
+  
+
+ +

Less than

+ +
+  
+      'field-name' => [ '_lt' => 10 ],
+  
+
+ +

Greater than

+ +
+  
+      'field-name' => [ '_gt' => 10 ],
+  
+
+ +

Less than or equal

+ +
+  
+      'field-name' => [ '_lte' => 10 ],
+  
+
+ +

Greater than or equal

+ +
+  
+      'field-name' => [ '_gte' => 10 ],
+  
+
+ +

Greater than or equal

+ +
+  
+      'field-name' => [ '_gte' => 10 ],
+  
+
+ +

In array

+ +
+  
+      'field-name' => [ '_in' => ['value_1', 'value_2'] ],
+  
+
+ +

Not in array

+ +
+  
+      'field-name' => [ '_nin' => ['value_1', 'value_2'] ],
+  
+
+ +

Contains

+ +
+  
+      'field-name' => [ '_contains' => 'must be contains this text' ],
+  
+
+ +

Not contain

+ +
+  
+      'field-name' => [ '_ncontains' => 'must not contain this text' ],
+  
+
+ +

Regex

+ +
+  
+      'field-name' => [ '_re' => '^test' ],
+  
+
diff --git a/example/templates/visibility/syntax.php b/example/templates/visibility/syntax.php new file mode 100644 index 00000000..62fff7cb --- /dev/null +++ b/example/templates/visibility/syntax.php @@ -0,0 +1,30 @@ +

Overview

+ +The conditional visibility functionality enables showing/hiding specific fields form based on other fields values. + +

Syntax

+ +To implement conditional visibility, use the following syntax: + +
+  
+    echo $fields->render_field('visibility-text', [
+      'condition' => [
+        'action'    => 'show',
+        'condition' => [
+          '_and' => [
+            ['field_name' => [ 'comparison_operator' => 'value']],
+            ['field_name' => [ 'comparison_operator' => 'value']]
+          ]
+        ]
+      ]
+    ]
+  
+
+ +
    +
  • field_name: the name of the field to which the conditional visibility applies.
  • +
  • field_value: the value of the field to which the conditional visibility applies.
  • +
  • action: either `show` or `hide`, depending on whether the field should be shown or hidden when the conditions are met.
  • +
  • condition: an array of conditions that must be met for the field to be shown/hidden. This array can contain an `_and` or `_or` key that specifies the logical operator to be used for the comparison, when no specified `_and` will be used.
  • +
diff --git a/tangible.config.js b/tangible.config.js index f07ea65d..aa6dfbdd 100644 --- a/tangible.config.js +++ b/tangible.config.js @@ -35,7 +35,19 @@ export default { { src: 'assets/src/contexts/beaver-builder/index.scss', dest: 'assets/build/beaver-builder/index.min.css' - } + }, - ] + /** + * Example + */ + { + src: 'assets/src/example/index.js', + dest: 'assets/build/example.min.js', + react: 'wp', + }, + { + src: 'assets/src/example/index.scss', + dest: 'assets/build/example.min.css', + }, + ], }