Script Library: 1238 scripts
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search

Archive version of: rebol-dom.r ... version: 55 ... inetw3dm 30-Jun

Amendment note: Updated: version 0.9 Roadmap: reduce global words. improve/shorten functions. || Publicly available? Yes

    	File: %rebol-dom.r
    	Date: 05-01-2021
    	Title: "Dialect Object Model"
        author: daniel murrill
    	Purpose: {
             Use a exstensible Rebol Markup function to parse DSL's that will return an
             easy to follow tagged key value Dialect Object Model  that allow different DSL's,
             programming languages to work with or together through a Central Processing
             interpreter. Yes Rebol/Core. A demo example of a HTML DSL is parsed by
             the Dialect Object Model to return valid HTML then processed by the %Mdlparser
             wich in return creates UI VID code. All scripting takes place in the DOM and then
             passed as VID code to Rebol/View. So instead of writing parse rules for DSL's, you
             work with your DSL as a DOM and immediatley began to write code for it as if it's a 
             programming language.}

library: [
        author: daniel murrill
    	level: 'intermediate
        platform: 'all
        type: Dialect
        domain: [html vid css json js array]
        tested-under: 'windows
        support: none
        license: none
        see-also: %Mdlparser.r
        version: 0.9

my-dialect: [

rebol-DOM: [{DSL-type="html/block" type="text/html"}]

lua-tbl = {[first] = "fred", [last] = "cougar"} 
#this-issue [{color: "purple" bgcolor: "orange"}]

var anObj = { 100: 'a', 2: 'b', 7: 'c', };

body: [{background-color="blue" text "white"} ]

p: [
    { "color":"#0000ff", "bgcolor":"yellow", "width" : "399", "height" : "100" "font-styles": "big" } 
 This is an example of an Dialect Oject Model
b: [
    {color = "Chocolate" bgcolor = "yellow"} 
 that can be written to look like json' html' javascript arrays' css'  
i: [
    {color = "green"} 
 VID' or plain old rebol ] 
 if you like.

hr: [{color="red" bgcolor="yellow"}]

div: [
    {width="399" bgcolor="orange"}  
b: [
    {color: "purple" bgcolor: "orange"} 
 Maybe it would be easier to write  
i: [
    {color: "green"}
 MakeDoc.r code but the purpose of this rebol DOM is']  

p: [
    {width "399" height= 200 
    style="color: red;  bgcolor: brown; border: 2x2;" 
b: [     
 to read or load as many well known dialects of coding styles as possible
 and write all of it as a single or seperate legible rebol series that's
 quickly manipulated' transcoded as html' and viewed as a VID. ]


document.: func [request-as][
		DOM: head DOM use*=*to-set-values 
		any [equal? block! type? data-node data-node: node: reduce [to-block data-node]]
		either equal? tag! type? data-node/1 [node-element: data-node/1
		node-element: data-node/1: build-tag to-block first parent-node: data-node
		var .style: = node-element *![style] *![.style]
                return node-element
return-tag-node: does [=: :equal? replace/all data-node "," " " 
		any [equal? block! type? data-node 
		data-node: node: reduce [to-block strip-obj-chars-from data-node]]
		any [equal? tag! type? node-element: data-node/1 attempt [
		replace/all node-element [=] [] 
		node-element: data-node/1: build-tag to-block node-element]
.style: []
equal?: :=
DOM: DSL: html: window: end-tag: ""
data-node: parent-node: node: none

.body: .hr: .p: .b: .i: .tr: .ul: .li: .area: .table: .td: .button: .input: .div: .font: .span: 0
node-obj: node-element: node-name: *name: use-methods: attr-name: attr-value: none

check: func [select-this][return first parse select-this none]

strip-chars-from: func [node-name][
		foreach char elemt-chars: ["<" ">" "{" "}" "(" ")" {"} "/" "." "[" "]" "," ":" ";" "," "="
		replace/all node-name char " "

strip-obj-chars-from: func [node-name][
		trim node-name if equal? #"^"" node-name/1 [remove node-name]
		foreach [-char +char] obj-chars: [#"^/" " " "  " " " 
		"," {"} {" : "} {: "} {":"} {: "} {":} {:} { " } "" 
		"{" "[" "}" "]" {""} {"} {  "} "" ":" " " { =} {=} {=} " " "'" ""
		replace/all node-name -char +char

getnodename: func [node-name][data-node: [""]
        DOM: head DOM =: :equal?
		either block! = type? node-name [
		insert/only data-node: [] node-name 
		any [attempt [node-element: data-node/1: build-tag data-node/1] node-element: data-node/1]
		node-name: to block! strip-chars-from form node-name
		count: any [attempt [count: pick find node-name integer! 1]1]
		nodename: to-word join  "." array-obj!: node-name/1 
		any [repeat ? count [data-node: first DOM: next node: find DOM nodename]
		print reduce ["node-name:" mold form node-name/1 "not found"]
		slf*: join node-name/1 count
		if '= = first DOM [data-node: first next DOM]
		return either tag! = type? data-node/1 [node-element: data-node/1][format-this return-tag-node as-js-object!] 

setnodename: func [old-name new-name][
		any [equal? this-name: check strip-chars-from old-name check node-element getnodename old-name]
		any [equal? none not find node-element new-name try [
		replace node-element to string! this-name new-name 
		replace data-node to-tag join "/" this-name  to-tag join "/" new-name] 
getattribute: func [attr-name][
			attr-value: none
			do compose/deep [attr-value: array-obj![(attr-name)]]
			print any [[attr-name attr-value] ["node-attribute:" mold form attr-name "not found"]

setattribute: func [attr-name new-attr][
                   *key: *value: none var node-element *![.style] 
                   do compose/deep [.style[(attr-name )]]
                   any [
                   if *value [replace node-element attr-name new-attr]
                   array-obj![repend [" " new-attr {="undefined"}]""]
                   print ["Must get a parent-node with this attribute: " attr-name]]
		   ;get-attributes node-element attempt [set-attributes]

setattributevalue: func [attr-name attr-value][
                   var node-element *![.style]
                   any [
                   if find node-element key: form attr-name [
		   do compose/deep [.style[(key)] `= (attr-value)]]	
		   array-obj![repend [" " attr-name {="}attr-value {"}]""]
		   print ["Must get a parent-node with this attribute: " attr-name]
		   ] ;get-attributes node-element attempt [set-attributes]

getElementByTagName: func [tag-Name selection [block!]][
                   dom: head dom 
                   repeat this first selection [
                   node-element: first data-node: first dom: next find dom to-word join "." tag-Name]
                   if [use-methods and not find node-element " ."][use-attr-as-methods yes]
                   use-methods: off        

look-deep-for: func [this in-node][
			foreach in-child-node in-node [
			if find/any mold node-element: in-child-node this [return node-element break]

getElementById: func [my-id][in-child-node: none 
			id: copy join "id*" form my-id =: :equal?
			foreach in-parent-node dom [
		if block! = type? in-parent-node [any [look-deep-for id in-parent-node
			foreach in-sibling in-parent-node [
		if block! = type? in-sibling [look-deep-for id in-sibling 

querySelecter: func [css-Selecter][in-child-node: none 
			id: rejoin [css-Selecter "*="] =: :equal?
			foreach in-parent-node dom [
		if block! = type? in-parent-node [any [look-deep-for id in-parent-node
			foreach in-sibling in-parent-node [
		if block! = type? in-sibling [look-deep-for id in-sibling 

.innerHTML: func [element][
			children: copy/part next data-node find/last data-node tag!
			either equal? tag! type? node-element [
			any [empty? element replace data-node children element]
			][print "The node-element has no innerHTML"]
use*=*to-set-values: does [=: func [value][any [value]]]

use-attr-as-methods: func [maybe [logic!]][
			if maybe [foreach [-char +char][{" } {" .} ".." "."][replace/all node-element -char +char]
			replace node-element " " " ."
			use-methods: yes

node-list: []

*!: func [var][set to-word check form node-element none set to-set-word form var :array-obj! =: :equal?]

var: func [var-data][node-element:  variable: "" node-name: :variable
			any [
			attempt [variable: first parse node-element: var-data "=:, "]
			attempt [if equal? block! type? var-data [variable: first node-element: var-data]] 
			attempt [variable: var-data node-element: ""]
			use*=*to-set-values variable: to-string variable
			any [
			not empty? node-element any [find node-list variable append node-list reduce 
                        [variable node-element: any [attempt [do :variable] " "]]]] 
                        any [equal? tag! type? node-element append node-list reduce [form variable node-element]] 
                        set to-word variable array-obj!: func [key] node: compose/deep copy get-array-obj!
			attempt [if not find variable "." [do load to-string reduce ["set " #"'" variable ". :array-obj!"]]]
			use-methods: off

return.: func [value][*value: load value 
			   foreach a *value [if not equal? word! type? a [
			   do *value: a ]]

.: func [value][either equal? block! type? *value [
				any [
				attempt [*value: next find *value value] *value: load value] 
				][ do compose/deep reduce [*value [(load value)]]

get-array-obj!: [*key: *value: none
			if not equal? tag! type? node-element [node-element: select node-list  
                       (form variable) ] 
			if inline: find/any node-element { style*=*"} [
			replace node-element inline replace inline {"} " "] 
			any [
                        if empty? form key [ 
                        return any [
                        if equal? tag! type? node-element [node-element]
			attempt [trim next find/tail head node-element first parse node-element ":= ,"]
                        next node-element]
			if all [*key: first to-block key not find node-element rejoin [" " *key " "] equal? integer! type? *key][
			remove value: parse strip-obj-chars-from mold copy 
                        any [attempt [to-block node-element] node-element] "<=`;: >"
			value: unique value replace/all replace/all value "[" [] "]" [] keys: length? value
			if odd? length? value [remove value]
			any [equal? 1 *key if equal? 2 *key  [*key: *key + 1] *key: *key + 2]		
			print ["   *key:" *key: pick value *key "*value:" *value: select value *key] 
			if find ["url!" "email!" "tag!"] mold type? key [ 
			attempt [key: to-string reduce load form parse/all replace/all copy key "." "/." "@/"] 
			foreach [-char +char][":" ":." "@" ":." ".." "."][replace/all key -char +char]
			from-method: parse/all key "." 
			attempt [*value: load first next find parse mold node-element "<{}=`;,#. []>" from-method/1
			remove from-method
			foreach element from-method [. element]]
			attempt [*value: load select parse strip-obj-chars-from mold copy node-element "<{} =`;,#>" 
			*key: trim head trim tail strip-chars-from form key]
			attempt [do head insert next copy key [node-element join]] 
                        attempt [do load key] 
			if not find node-element key [*key: *value: none]] ;[print [*key " not found"]]

`=: func[attr-value][
			any [
			attempt [replace node-element *value do reform load attr-value]
			replace node-element form *value form attr-value
			replace node-element mold form *value mold attr-value
			attempt [*value: select node-element *key: load key] 

node-element: *node-name: *name: array-obj!: key: *key: *value: ""

markup-DOM: func [DSL][
use-methods: string-is-data: no =: :equal?
		either block! = this-type?: type? DSL [
			DOM: copy/deep DSL: load replace/all replace/all mold DSL "__" " " {=""} {="undefined"}
			any [
			find DSL [rebol-DOM: [{DSL-type="html/block" type="text/html"}]]
			insert DSL [rebol-DOM: [{DSL-type="html/block" type="text/html"}]]]
get-data: func [data][	
		find-end-tag: find data get-word!
			either find-end-tag [
			replace data first find-end-tag  to-tag mold to-refinement first find-end-tag
			insert tail data to-tag join "/" any [*name *node-name]
repeat in-data data [
		if set-word! = type? in-data [*node-name: copy form in-data
			replace data in-data node-name: to-word join "." *node-name
		if string! = type? in-data [
			strip-obj-chars-from in-data 
			insert in-data next reform [node-name " "]
			child: build-tag to-block in-data 
			foreach [a b][{=" } "={" {" "} "*}" "*" {"}][replace/all child a b]
			replace data in-data child
		if block! = type? in-data [
			get-next in-data

get-next: func [in-data][
	repeat data in-data [
		if set-word! = type? data [*name: copy form data
			replace in-data data node-name: to-word join "." *name
		if string! = type? data [
			strip-obj-chars-from data  
			insert data next reform [node-name " "]
			child: build-tag to-block data 
			foreach [a b][{=" } "={" {" "} "*}" "*" {"}][replace/all child a b]
			replace in-data data child
		if block! = type? data [get-data data
			set to-word *node-name data]
		] find-end-tag: find in-data get-word!
			either find-end-tag[
			replace in-data first find-end-tag  to-tag mold to-refinement first find-end-tag
			insert tail in-data to-tag join "/" any [*node-name *name]
			clear *node-name]

repeat with-this-data DSL [
		if issue! = type? with-this-data [
			node-name: to-word join "." *name: copy form with-this-data
		if set-word! = type? with-this-data [
			replace DSL with-this-data node-name: to-word join "." *name: copy form with-this-data
		if word! = type? with-this-data [
			either '= = with-this-data [string-is-data: yes replace DSL with-this-data ""
			*name: copy form with-this-data
			replace DSL with-this-data node-name: to-word join "." with-this-data] 
			replace DSL reduce [node-name node-name] node-name
		if string! = type? with-this-data [
			either string-is-data [
			replace DSL with-this-data rejoin [node-name " " with-this-data]
			created-data: copy with-this-data
			strip-obj-chars-from created-data  
			insert created-data reduce [node-name " "]
			child: build-tag to-block remove created-data 
			foreach [a b][{=" } "={" {" "} "*}" "*" {"}][replace/all child a b]
			replace DSL with-this-data child
			string-is-data: no
		if block! = type? with-this-data [ 
			get-data with-this-data
		if get-word! = type? with-this-data [
			replace DSL with-this-data to-tag mold to-refinement *name
		    replace/all dsl "" [] DOM: copy DSL
		    view center-face layout [
			backcolor black space 8
			h3 yellow "Need a Dialect Object Model as type! block"
			text gray "Your DSL type is: " text red bold form :this-type? 
			text gray "Some DOM functions will not work properly"]

format-this: func [node-element with-these-chars][
			foreach [-char +char] with-these-chars [
			replace/all node-element -char +char] 
			do last with-these-chars

as-js-object!: [
			{ #} "" 
			{="} {: "} "={" ": {" {""} {"} {" } {", } {", "} {","}
			[if equal? tag! type? node-element [replace node-element " " ": {"] 
			replace/all node-element { "} {: "}
			replace/all node-element "::" ":"
			replace/all node-element " : " ": "
			if equal? tag! type? node-element [append node-element "}"]]

.as-js-object: does [attempt [format-this node-element as-js-object!]]

return-html-dsl: does [
		html: copy form any [find head DOM "<body" head DOM]	
		replace/all replace/all html "<." "<" "' " ", "
		replace/all replace/all html "{" "<" "}" ">" 
		foreach n-name [
						".body " ".hr " ".p " ".b " ".i " ".? " 
						".tr " ".table " ".td " ".button " ".input "
						".div " ".ul " ".li " ".font " ".span "
						".img " ".a " ".strong " " ." ".hr " "lua-tbl "
						][replace/all html n-name " "
		append replace/all html </body> "" </body>
		html: any [find html "<body" html]

markup-DOM my-dialect

getElementById 'my-p1
document. getnodename {"div"[1]}getattribute "bgcolor"  
document. getnodename{("p")[2]} .style[width] `= "block-build"

setattributevalue('height) "1000"

    getattribute "height"

setnodename {hr[1]} "listview"

document. getnodename{("p")[1]}.style[font-styles] `= "block-build"

data-node: do to-path [DOM .p .b] 

document.(data-node).style[color] `= "bluberry" 

    setattribute('i-style) "silly" 

; Some examples of selecting keys and values set to variables as an array object.
; good to use with anything that's structured with {} , or "". It also can be used with [].
;IE (javascript, json objects/arrays.. lua table/arrays.. associative arrays etc.) 
;even DOM's and rebol data.

;The insert, remove, append, and lookup of a group (a node-element) by way of a Var (variable)
;is done as a data-node and syntactically as an associative array. The associattion of key 
;values is done by index in Rebol therefore there is no need for the overhead of objects or 
;multi.dim arrays. But the cool part is you can do what ever you like!

;You must use use*=*to-set-values before setting the first variable.
;it will use = to set any value to a variable (a set-word) 
;`= this literal equal is used to set values to keys. Feel free to use
;different function symbols for any !op function.  


;variables and there values (node-elements) that are'nt tags are sent and fetched from a
;node-list block for demo purposes. They will be appended to the DOM later.

;The variable name and the first word in it's value must be the same in order for
;the variable to be automatically bound to the node-element and array-obj!() function.

var hello: = {hello 100:"a" 2: "two words" 7="c"}


hello[2] `= "a to z"


;insert dots, "." to keys to be used as methods to get and set values.
;the to-methods function not created yet.sorry!.dot.

;use-methods: yes  

var app: = document.(getElementByTagName('p)[2]).style[bgcolor]`="green"
    .innerHTML("Lets change this, shall we.")

;You must call/associate the array-obj! with this variable name
;because the variable name "app" and the node-name/TagName "p"
;are not the same.


;or app: :array-obj!

;the style.obj! is auto created by using the document. function


;is the same as calling, but without the cell(block) this time. 

app "bgcolor"

;Write values however. they are handled by the get-array-obj! as any-type.


app[.width] `= "curly quew"

app ("bgcolor") `= to-string [purple]

;when you need to create a variable to use with an array-obj!

poppy: {100:"a" street : "backside" 7="c"}

var 'poppy ;local or global hoisting: must move to head.
head poppy[] 

var join {poppy2 } {200:"a" street : "backside" 7="c"}

poppy[street]`="old rover rd."

poppy[7] `= "cities"

;You do not need a variable word. If you apply Var to your 
;value, the variable will always be the first word found in 
;the value (node-element) followed by ": =", " =", ": ", or " " 
;you can allso access it with array-obj![]. 

var {
	1: a
	2: b
	3: c


var <new-var: 1: a 2: b 3: c> ;tags are allways node-elements. 


;VAR values can also be set to an arrayed object.

var poppy3: = [poppy3 #100:"a" street: "backside" #7:"c"]

;Even though it's not the best idea to use integers as keys, you can.
;it can encourage mistakes when trying to get by index...  poppy2[3]
;But this is Rebol, so we will roll with this...
;You must always start cell number keys with the pound sign. "#"

poppy3[street:]`= "old rover rd."

poppy3[#7:]`= "cities"

;DOM variables are renamed to start with a dot "." so not to be confused with words.
;this will change in the future. All DOM Var's will be type! set-word!.

;Remember when calling a node-name and using that name as the variable,

var .anObj: = getnodename "anobj"

;you dont have to call/associate the array-obj! with this variable name
;but the variable name must began with a dot. IE "."

.anobj[7] ;to be fixed

;but with 

var anObj: = getnodename "anobj" 

;you do

*![anObj]  ;or anObj: :array-obj!
anobj[7] ;to be fixed

;after calling some variable[key]/array-obj![key], you can replace the key and value.

replace replace node-element *key 'this-key *value {'some-value'}


var 'try-this 

try-this.[insert (add 50 50) ","]


try-this.[append ("new-value")","]

try-this[.innerHTML("change me please") ""]

;a roadmap for javascripting with the Dialect Object Model (DOM).

;A quick way to change op! functions between rebol and javascript.

js-op!: does [x: :* *: :do console.log: :print var 'use js-do: :js-op!] 
reb-op!: does [*: :x =: :equal? reb-do: :reb-op!]

;This code...

function welcomeSite() {
   siteMessage = "Welcome to the...";
   Message-type: "Welcome"
   var siteMessage;

siteMessage.append("Rebolution") siteMessage

;Is changed to this code...


'function! welcomeSite: *{
     siteMessage: = "Welcome to the... ";
     Message-type: "Welcome"
     do reb-op! alert(siteMessage)
     var 'siteMessage;

;Hey you can load welcomeSite from a DOM or files with variables, functions, key-values or Rebol code:


;to use the welcomeSite Message-type in your code.

    var say-hi-to: = Message-type
    console.log(["This is a message to" say-hi-to "you."]);
siteMessage.[append("Rebolution.")#"^/"] siteMessage.[print]

var first-obj: = {first-obj: first-name: "Brigsby" last-name: "Backfort"}

var second-obj: = {second-obj: first-name: "Krystal" last-name: "Frontbunker"}

first-obj[last-name]`="FortBack"  first-obj[]

second-obj[last-name]`="Bunkerfront"  second-obj[]

Var contacts: = {Bestfriend: "[{"first": "Besty", "last": "Bestest", "number": 777-9211}]"}

;Does Var persist if not destroyed? YES.

;and then back to Rebol

reb-do 2 * 2

;Create a DOM from a nested javascript object DSL, search and update.
;A return-js-object-dsl() function will return the new js-object.
;Mold blocks and load strings to do quick formating like remove all ",".
markup-DOM load replace/all {data: [
    fullName: "James Zhan",
    address: {
        city: "Shenzhen",
        state: {
            name: "Guangdone",
            abbr: "CN""
        country: {
            local-name: "Guangdone",
            abbr: "CN"
			id: "jazzy"
    hobbies: ["coding", "reading"],
    projects: [
        { name: "form2json", language: "javascript", popular: true },
        { name: "peony", language: "ruby" }
} "," ""


var here: dom/.data/.projects/1



format-this node-element as-js-object!

;using paths to get node-elements

var address: dom/.data/.address 

;using nested indexing by keys to get/set node-element values

address[state].[country].[abbr] `="pop"

clear node-list


;And a cool but very simple trick in Rebol. For non-rebolers
;this IS code and data mixed together. First it's ALL string data.

var num: {num: a: 1 b: 2 c: 3}
;Rember, the *num value is a string, not an object.
amount: num[a]`= [amount '+ 15]
;And the new *formed() value is a [string: "1" word: '+ integer: 15]

;The DO() function binds each piece to the interpreter global symbols, and voila!


;And now Var methods with linked node-element lists, a mixture of object and arrays
;in a simple DSL keeping track of data updates with set limits. Don't try to undestand
;it, it's silly. 

;methods are written as messages sending or recieving. So lets use a form of
;urls and emails.

get-numbers@.has-a.variable-a ;or
get-numbers:.has-a.variable-a ; cause theres a set-word in there.

;all methods must either have a set-word or "@" appended
;to the tail to be valid  Var methods.

;if the word to start the method is a set-word *get-numbers: 
;the words to continue the method can each have "@" appended to them.


;they will be looked up as Var words or set-words or else there
;is no vectoring/linking. Yes if it's a string word it will be given local context.
;The string word will be matched as a pointer to it's literal word 
;to return the correct Var or local value.

clear node-list

var check-if: = [
                check-if start: 'at-head 
                stop: 'at-tail 
                get-numbers: numbers  ;this is linked to local Var numbers

var numbers: {
		start-at: 1 
		has-a: limit  ;this is linked to local Var limit

var limit: = {
        variable-a: "yes" 
		info: "I am a returned method" 
		 ;using ancester Var to get info:
        variable-b: ['function.(js-op!){console.log{variable-a"info"}}]
        } *![limit]

amount: numbers{start-at} `= {amount '+ 15}

;don't create a vaviable (*amount) to GC.

numbers{start-at} `= {numbers{start-at} '+ 15}

increased: numbers[start-at]

do check-if[get-numbers:][increased]



;Using .[] or .{} to load the last known variable and retun its node-element

check-if(get-numbers@has-a) . {
                                    setattributevalue "last-variable" "value-off"

;place *fetch, "@" before or after any key. use it in place of the set/get-word #":"

check-if get-numbers@.has-a.variable-a

;And now get-numbers: , has-a, and variable-a becomes Var's once linked to
;only if they have following data. Now we can use them in methods with the
;children *value in its data.


;Let's send get-numbers:has-a.variable-a data, wich is "yes" to

daniel: "get-numbers@" 
mindsping: "has-a" 
.com: ".variable-a"

send check-if <>

;the node-element is now *limit[]

=: :equal?

if (numbers has-a:variable-b). {
				print "this worked"
else: :.

either 16 = numbers<start-at>. {
	print "yessers"
       } else {  
        print "nossers"

setattributevalue 'reset 'check-if



;This is a quick get by index example
;on strings, using parse and select
;The same string value, ie. node-element
;key can be selected by *key or index now. 
;The code will be updated for nested selections later.





;You can use any ancestor Var, (ie. variable-a) 
;in a Var method (ie. variable-b) to return a *value.

return. limit[variable-b]



;A method is just putting the name of a Var in another Var as a key value pair.
;You don't need objects, parsers, classes, or tricked out functions to use your
;DSL's. To start you just look up or set everything by key value pairs.
;methods are just a quicker way to get to local associated data.