1 module deepmagic.dom.xml.element; 2 3 import deepmagic.dom; 4 5 class Element : Item{ 6 Tag tag; /// The start tag of the element 7 Item[] items; /// The element's items 8 Text[] texts; /// The element's text items 9 CData[] cdatas; /// The element's CData items 10 Comment[] comments; /// The element's comments 11 ProcessingInstruction[] pis; /// The element's processing instructions 12 Element[] elements; /// The element's child elements 13 bool[string] _classes = null; 14 Sass[] _sasses = null; 15 16 this(string name, string interior=null){ 17 this(new Tag(name)); 18 if (interior.length != 0) opCatAssign(new Text(interior)); 19 } 20 21 this(const(Tag) tag_){ 22 this.tag = new Tag(tag_.name); 23 tag.type = TagType.EMPTY; 24 foreach(k,v;tag_.attr) tag.attr[k] = v; 25 tag.tagString = tag_.tagString; 26 } 27 28 void opCatAssign(Text item){ 29 texts ~= item; 30 appendItem(item); 31 } 32 33 void opCatAssign(CData item){ 34 cdatas ~= item; 35 appendItem(item); 36 } 37 38 void opCatAssign(Comment item){ 39 comments ~= item; 40 appendItem(item); 41 } 42 43 void opCatAssign(ProcessingInstruction item){ 44 pis ~= item; 45 appendItem(item); 46 } 47 48 void opCatAssign(Element item){ 49 elements ~= item; 50 appendItem(item); 51 } 52 53 void opCatAssign(string item){ 54 this ~= new Text(item); 55 } 56 57 import deepmagic.sass; 58 typeof(this) add_sass(Sass sass){ 59 this._sasses ~= sass; 60 this.refresh_tag(); 61 return this; 62 } 63 64 typeof(this) add_class(string class_name, bool value = true){ 65 this._classes[class_name] = value; 66 this.refresh_tag(); 67 return this; 68 } 69 70 typeof(this) add_class(string[] class_names){ 71 foreach(int key, string class_name; class_names){ 72 this.add_class(class_name); 73 } 74 return this; 75 } 76 77 typeof(this) remove_class(string class_name){ 78 if(class_name in this._classes){ 79 this._classes.remove(class_name); 80 } 81 this.refresh_tag(); 82 return this; 83 } 84 85 typeof(this) add_sass(Sass[] sasses){ 86 foreach(int key, Sass sass; sasses){ 87 this.add_sass(sass); 88 } 89 return this; 90 } 91 92 void opCatAssign(Sass sass){ 93 this.add_sass(sass); 94 } 95 96 protected void refresh_tag(){ 97 string c = ""; 98 foreach(int i, Sass sass; this._sasses){ 99 c ~= sass.name ~ " "; //TODO: This leaves a trailing " " at the end of the string, which is sloppy. Clean it up. 100 } 101 foreach(string s, bool v; this._classes){ 102 c ~= s ~ " "; //TODO: This leaves a trailing " " at the end of the string, which is sloppy. Clean it up. 103 } 104 this.tag.attr["class"] = c; 105 } 106 107 private void appendItem(Item item){ 108 items ~= item; 109 if (tag.type == TagType.EMPTY && !item.isEmptyXML) 110 tag.type = TagType.START; 111 } 112 113 void parse(ElementParser xml){ 114 xml.onText = (string s) { opCatAssign(new Text(s)); }; 115 xml.onCData = (string s) { opCatAssign(new CData(s)); }; 116 xml.onComment = (string s) { opCatAssign(new Comment(s)); }; 117 xml.onPI = (string s) { opCatAssign(new ProcessingInstruction(s)); }; 118 119 xml.onStartTag[null] = (ElementParser xml){ 120 auto e = new Element(xml.tag); 121 e.parse(xml); 122 opCatAssign(e); 123 }; 124 125 xml.parse(); 126 } 127 128 override bool opEquals(Object o){ 129 const element = toType!(const Element)(o); 130 auto len = items.length; 131 if (len != element.items.length) return false; 132 foreach (i; 0 .. len){ 133 if (!items[i].opEquals(cast()element.items[i])) return false; 134 } 135 return true; 136 } 137 138 override int opCmp(Object o){ 139 const element = toType!(const Element)(o); 140 for (uint i=0; ; ++i){ 141 if (i == items.length && i == element.items.length) return 0; 142 if (i == items.length) return -1; 143 if (i == element.items.length) return 1; 144 if (items[i] != element.items[i]) 145 return items[i].opCmp(cast()element.items[i]); 146 } 147 } 148 149 override size_t toHash() const{ 150 size_t hash = tag.toHash(); 151 foreach(item;items) hash += item.toHash(); 152 return hash; 153 } 154 155 const{ 156 string text(DecodeMode mode=DecodeMode.LOOSE){ 157 string buffer; 158 foreach(item;items){ 159 Text t = cast(Text)item; 160 if (t is null) throw new DecodeException(item.toString()); 161 buffer ~= decode(t.toString(),mode); 162 } 163 return buffer; 164 } 165 166 override string[] pretty(uint indent=2){ 167 168 if (isEmptyXML) return [ tag.toEmptyString() ]; 169 170 if (items.length == 1){ 171 Text t = cast(Text)(items[0]); 172 if (t !is null) 173 { 174 return [tag.toStartString() ~ t.toString() ~ tag.toEndString()]; 175 } 176 } 177 178 string[] a = [ tag.toStartString() ]; 179 foreach(item;items){ 180 string[] b = item.pretty(indent); 181 foreach(s;b) 182 { 183 a ~= rightJustify(s,count(s) + indent); 184 } 185 } 186 a ~= tag.toEndString(); 187 return a; 188 } 189 190 override string toString(){ 191 if (isEmptyXML) return tag.toEmptyString(); 192 193 string buffer = tag.toStartString(); 194 foreach (item;items) { buffer ~= item.toString(); } 195 buffer ~= tag.toEndString(); 196 return buffer; 197 } 198 199 override @property bool isEmptyXML() { return items.length == 0; } 200 } 201 }