#define xml_init //xml_init() globalvar //object globals xml_node, xml_doctype, xml_declaration, xml_document, xml_element, xml_text, //other globals xml_debug, ; //make node objects xml_node = object_add(); xml_doctype = object_add(); xml_declaration = object_add(); xml_document = object_add(); xml_element = object_add(); xml_text = object_add(); //define parental relationships on object level object_set_parent(xml_doctype , xml_node); object_set_parent(xml_declaration , xml_node); object_set_parent(xml_document , xml_node); object_set_parent(xml_element , xml_node); object_set_parent(xml_text , xml_node); //other global variables xml_debug = 1; // debug: 0 = no messages; 1 = errors; 2 = notices return(true); #define xml_clear //xml_clear([document:id]) var argument_num;if(!variable_local_exists('argument_count')){var a,i;a=0;for(i=0;i<16;i+=1){if(string(argument[i])!="0"){a=i+1;}}argument_num=a;}else{argument_num=argument_count;} // Have all nodes that meet the document requirements (if any) commit suicide var document; document = false; if(argument_num>0){ document = argument0; } with(xml_node){ if(self.document==document || !document){ //ds_map_destroy(attr_map) instance_destroy(); } } return(true); #define xml_read //xml_read(xml:str,[parent:id]) var argument_num;if(!variable_local_exists('argument_count')){var a,i;a=0;for(i=0;i<16;i+=1){if(string(argument[i])!="0"){a=i+1;}}argument_num=a;}else{argument_num=argument_count;} // Read XML as string and convert it to node instances var xml,document,parent, stack,node,text_node, buffer,buffer_start,offset, type,name, i,c,tag_end, closing,autoclose, txt,cdata,comment,comments; xml = argument0; parent = noone; if(argument_num>1){ parent = argument[1]; } //convert tabs to spaces xml=string_replace_all(xml,chr(9)," "); //clean up leading spaces while(string_char_at(xml,1)==" "){ xml = string_delete(xml,1,1); } while(string_pos(chr(10)+" ",xml)){ xml=string_replace_all(xml,chr(10)+" ",chr(10)); } while(string_pos(chr(13)+" ",xml)){ xml=string_replace_all(xml,chr(13)+" ",chr(13)); } //clean up double spaces while(string_pos(" ",xml)){ xml=string_replace_all(xml," "," "); } // Set document (no parent = new document) if(parent == noone){ document = xml_node_create(xml_document,"document",noone,self); parent = document; }else{ document = parent.document; } buffer = xml; buffer_start = 0; stack = ds_stack_create(); // read contents while(str_find(buffer,"<")){ buffer_start += str_find(buffer,"<"); buffer = str_after(buffer,"<"); type = xml_element; // DEFAULT = tag opening offset = 0; closing = false; cdata = ""; comments = 0; switch(string_char_at(buffer,1)){ // Check first character for special node_type case "!": // doctype declaration/definition type = xml_doctype; offset = 1; // filter out and capture cdata if(string_pos("[CDATA[",buffer)==1){ cdata = str_between(buffer,"[CDATA[","]]"); buffer_start += string_length(cdata)+9; buffer = str_outside(buffer,"[CDATA[","]]"); } // filter out and capture comments while(str_is_first(buffer,"--",">")){ comment[comments] = str_between(buffer,"--","--"); buffer_start += string_length(comment[comments])+4; buffer = str_outside(buffer,"--","--"); comments += 1; } break; case "?": // XML declaration type = xml_declaration; offset = 1; break; case "/": // tag closing closing = true; offset = 1; break; case " ": // whitespace offset offset = 1; break; } // determine tag end position if(str_is_first(buffer,"/>",">")){ tag_end = str_find(buffer,"/>"); autoclose = true; }else{ tag_end = str_find(buffer,">"); autoclose = false; } // get name and attributes if(str_is_first(buffer," ",tag_end)){ name = string_lower(str_between(buffer,offset," ")); attrs = str_between(buffer," ",tag_end); }else{ name = string_lower(str_between(buffer,offset,tag_end)); attrs = ""; } // create or close the node if(!closing){ // create a node node = xml_node_create(type,name,parent,document,attrs); node.opentag_start = buffer_start; node.opentag_end = buffer_start+tag_end; node.closetag_start = buffer_start; node.closetag_end = buffer_start+tag_end; node.inner_html = ""; node.outer_html = ""; if(type == xml_element && !autoclose){ // if it remains opened node.node_open = true; ds_stack_push(stack,node); parent = node; if(xml_debug>1)show_debug_message( "Pushing node #"+string(node)+" on stack." ); }else{ node.outer_html = str_between(xml,node.opentag_start-1,node.closetag_end+1); } if(type==xml_doctype){ node.cdata = cdata; for(i=0;i1)show_debug_message( "Finished node: name = '"+name+ "'; attrs = ["+attrs+"]; type = "+string(type)+ "; open = "+string(node.node_open)+ "; comments = "+string(comments) ); }else{ // close a node /* Note: Variable "name" (name of currently closing tag) is only used to validate. The node that's actually closing is the one that was last opened. */ node = ds_stack_pop(stack); if(!node){ if(xml_debug>0)show_debug_message( "Malformed XML: closing "+name+" while no tags are open. " ); continue; } parent = node.parent; if(name != node.node_name || !node.node_open){ if(xml_debug>0)show_debug_message( "Malformed XML: closing "+name+" instead of "+node.node_name+". " ); } // update node node.node_open = false; node.closetag_start = buffer_start; node.closetag_end = buffer_start+tag_end; node.inner_html = str_between(xml,node.opentag_end,node.closetag_start); node.outer_html = str_between(xml,node.opentag_start-1,node.closetag_end+1); if(xml_debug>1)show_debug_message( "Closing element node "+name+": from = " + string(node.opentag_start)+", " + string(node.opentag_end)+"; to = " + string(node.closetag_start)+", " + string(node.closetag_end) ); } // make a textnode txt = str_between(buffer,">","<"); // grab contents c = string_char_at(txt,1); while(c==" " || c==chr(10) || c==chr(13)){ // remove leading spaces and newlines txt = string_delete(txt,1,1); c = string_char_at(txt,1); } if(txt!=""){ // if not empty, make textnode text_node = xml_node_create(xml_text,"text",parent,document); text_node.node_value = txt; } } ds_stack_destroy(stack); return(document); #define xml_node_create //xml_node_create(type:obj,name:str,parent:node,document:id,[attrs:str]) var argument_num;if(!variable_local_exists('argument_count')){var a,i;a=0;for(i=0;i<16;i+=1){if(string(argument[i])!="0"){a=i+1;}}argument_num=a;}else{argument_num=argument_count;} // Make a new node var node,type,name,parent,document,attrs; type = argument0; name = argument1; parent = argument2; document = argument3; attrs = ""; if(argument_num>4){ attrs = argument[4]; } node = instance_create(0,0,type); node.parent = parent; node.document = document; node.node_name = name; node.node_type = type; node.child_id = 0; node.child_num = 0; node.node_open = false; node.attr_map = -1; if(node.parent && instance_exists(node.parent)){ node.parent.child_nodes[node.parent.child_num]=node; node.parent.child_num+=1; node.child_id = node.parent.child_num; if(xml_debug>1)show_debug_message( "Created node "+string(node)+": name="+name+"; type="+string(type)+"; parent="+string(parent)+"; child_id="+string(node.child_id)+"; attrs="+string(attrs) ); } if(type==xml_element && attrs!=""){ xml_set_attributes(node,attrs); } if(type==xml_doctype){ node.doctype_attrs = attrs; } return(node); #define xml_set_attributes //xml_set_attributes(node:id,attributes:str) // Convert a string with attributes to node variables var node,attr,buffer,attrname,attrval,quote; node = argument0; attr = argument1+" "; buffer = attr; /* if(node.attr_map < 0){ node.attr_map = ds_map_create(); } */ do{ // skip leading whitespaces while(string_char_at(buffer,0)==" "){ buffer = string_delete(buffer,1,1); } // get attribute name if(str_is_first(buffer," ","=")){ attrname = str_before(buffer," "); buffer = str_after(buffer," "); has_value = false; }else{ attrname = str_before(buffer,"="); buffer = str_after(buffer,"="); has_value = true; } // get attribute value quote = '"'; if(attrname!="" && attrname!=" " && attrname!="/"){ quote = str_first(buffer,"'",'"'); // determine quote type if(str_is_first(buffer,quote," ")){ attrval = str_between_ext(buffer,quote,quote,0,true,false); // _ext version to allow escaped quotes buffer = str_after_ext(buffer,quote,quote,true); }else{ if(has_value){ attrval = str_before(buffer," ") }else{ attrval = ""; } buffer = str_after(buffer," "); } with(node){ variable_local_set("attr_"+attrname,attrval); //ds_map_set(attr_map,attrname,attrval); if(xml_debug>1)show_debug_message(" - var_set(attr_"+attrname+","+attrval+")"); } } //if(!show_question("buffer: "+buffer+"##name: "+attrname+"#val: "+attrval)){return(0)}; }until(!str_find(buffer," ")); #define xml_get_attribute //xml_get_attribute(element:id,attribute:str) var element,attr; element = argument0; attr = argument1; with(element){ return(var_get("attr_"+attr)); } #define xml_element_select //xml_element_select(attribute:str,value:str/real,[num:real]) var argument_num;if(!variable_local_exists('argument_count')){var a,i;a=0;for(i=0;i<16;i+=1){if(string(argument[i])!="0"){a=i+1;}}argument_num=a;}else{argument_num=argument_count;} var attr,val,num,i; attr = argument0; val = argument1; num = 1; if(argument_num>2){ num = max(1,argument[2]); } i=0; with(xml_element){ if(var_get("attr_"+attr)==val){ i+=1; if(i==num){ return(id); } } } return(false); #define xml_element_search //xml_element_search(attribute:str,needle:str,[num:real]) var argument_num;if(!variable_local_exists('argument_count')){var a,i;a=0;for(i=0;i<16;i+=1){if(string(argument[i])!="0"){a=i+1;}}argument_num=a;}else{argument_num=argument_count;} var attr,val,num,i; attr = argument0; val = argument1; num = 1; if(argument_num>2){ num = max(1,argument[2]); } i=0; with(xml_element){ if(string_pos(val,var_get("attr_"+attr))){ i+=1; if(i==num){ return(id); } } } return(false); #define xml_is_ancestor //xml_is_ancestor(node:id,ancestor:id/str) var node,ancestor; node = argument0; ancestor = argument1; with(node){ if(var_is_local("parent")){ if(is_string(ancestor)){ if(node_name==ancestor){ return(true); } }else{ if(parent==ancestor){ return(true); } } return(xml_is_ancestor(parent,ancestor)); } } return(false); #define xml_find_ancestor //xml_find_ancestor(node:id,ancestor:str) var node,ancestor; node = argument0; ancestor = argument1; with(node){ if(node_name==ancestor){ return(node); }else{ return(xml_find_ancestor(parent,ancestor)); } } return(false);