1 module uim.html.obj; 2 3 import uim.html; 4 5 class DH5Obj { 6 this() { _init; } 7 this(string content) { this().content(content); } 8 this(DH5Obj[] content...) { this().content(content); } 9 this(DH5Obj[] content) { this().content(content); } 10 this(DH5 content) { this().content(content); } 11 12 this(string id, string content) { this().id(id).content(content); } 13 this(string id, DH5Obj[] content...) { this().id(id).content(content); } 14 this(string id, DH5Obj[] content) { this().id(id).content(content); } 15 this(string id, DH5 content) { this().id(id).content(content); } 16 17 this(string id, string[] classes) { this().id(id).classes(classes); } 18 this(string id, string[] classes, string content) { this(id, classes).content(content); } 19 this(string id, string[] classes, DH5Obj[] content...) { this(id, classes).content(content); } 20 this(string id, string[] classes, DH5Obj[] content) { this(id, classes).content(content); } 21 this(string id, string[] classes, DH5 content) { this(id, classes).content(content); } 22 23 this(string id, string[string] someAttributes) { this().id(id).attributes(someAttributes); } 24 this(string id, string[string] someAttributes, string content) { this(id, someAttributes).content(content); } 25 this(string id, string[string] someAttributes, DH5Obj[] content...) { this(id, someAttributes).content(content); } 26 this(string id, string[string] someAttributes, DH5Obj[] content) { this(id, someAttributes).content(content); } 27 this(string id, string[string] someAttributes, DH5 content) { this(id, someAttributes).content(content); } 28 29 this(string id, string[] classes, string[string] someAttributes) { this(id, classes).attributes(someAttributes); } 30 this(string id, string[] classes, string[string] someAttributes, string content) { this(id, classes).attributes(someAttributes).content(content); } 31 this(string id, string[] classes, string[string] someAttributes, DH5Obj[] content...) { this(id, classes).attributes(someAttributes).content(content); } 32 this(string id, string[] classes, string[string] someAttributes, DH5Obj[] content) { this(id, classes).attributes(someAttributes).content(content); } 33 this(string id, string[] classes, string[string] someAttributes, DH5 content) { this(id, classes).attributes(someAttributes).content(content); } 34 35 this(string[] classes) { this().classes(classes); } 36 this(string[] classes, string content) { this(classes).content(content); } 37 this(string[] classes, DH5Obj[] content...) { this(classes).content(content); } 38 this(string[] classes, DH5Obj[] content) { this(classes).content(content); } 39 this(string[] classes, DH5 content) { this(classes).content(content); } 40 41 this(string[] classes, string[string] someAttributes) { this(classes).attributes(someAttributes); } 42 this(string[] classes, string[string] someAttributes, string content) { this(classes, someAttributes).content(content); } 43 this(string[] classes, string[string] someAttributes, DH5Obj[] content...) { this(classes, someAttributes).content(content); } 44 this(string[] classes, string[string] someAttributes, DH5Obj[] content) { this(classes, someAttributes).content(content); } 45 this(string[] classes, string[string] someAttributes, DH5 content) { this(classes, someAttributes).content(content); } 46 47 this(string[string] someAttributes) { this().attributes(someAttributes); } 48 this(string[string] someAttributes, string content) { this(someAttributes).content(content); } 49 this(string[string] someAttributes, DH5Obj[] content...) { this(someAttributes).content(content); } 50 this(string[string] someAttributes, DH5Obj[] content) { this(someAttributes).content(content); } 51 this(string[string] someAttributes, DH5 content) { this(someAttributes).content(content); } 52 53 // this(DH5Obj[] content...) { this().content(content); } 54 55 public void _init() { 56 _css = CSSRules; 57 _classes = null; 58 _attributes = null; 59 _html = []; 60 _js = null; 61 } 62 63 mixin(TProperty!("bool", "single", "false")); 64 mixin(TProperty!("string", "tag")); 65 mixin(TProperty!("string", "id")); 66 mixin(TProperty!("DH5Obj[]", "html")); 67 68 protected string _js; 69 @property string js() { return _js; } 70 O js(this O)(string[] codes...) { foreach(c; codes) _js ~= c; return cast(O)this; } 71 O js(this O)(DJS[] codes...) { foreach(c; codes) _js ~= c.toString; return cast(O)this; } 72 73 /// Classes of HTML element 74 protected string[] _classes; 75 auto classes() { return _classes.sort.array; } 76 O classes(this O)(string[] addValues...) { foreach(v; addValues) _classes ~= v; return cast(O)this; } 77 O classes(this O)(string[] addValues) { foreach(v; addValues) _classes ~= v; return cast(O)this; } 78 O clearClasses(this O)() { _classes = []; return cast(O)this; } 79 unittest { 80 assert(H5Obj.classes(["a", "b"]).classes == ["a", "b"]); 81 assert(H5Obj.classes(["b", "a"]).classes == ["a", "b"]); 82 assert(H5Obj.classes("a", "b").classes == ["a", "b"]); 83 assert(H5Obj.classes("b", "a").classes == ["a", "b"]); 84 } 85 86 /// Attributes of HTML element 87 mixin(XStringAA!"attributes"); 88 unittest { 89 assert(H5Obj.attributes(["a": "b"]).attributes == ["a": "b"]); 90 } 91 92 // Attribute 93 string attribute(string name) { return (name in _attributes? _attributes[name] :""); } 94 O attribute(this O)(string name, string value) { _attributes[name] = value; return cast(O)this; } 95 O attribute(this O)(string name, bool value) { if (value) attribute(name, "true"); else attribute(name, "false"); return cast(O)this; } 96 O removeAttribute(this O)(string name) { _attributes.remove(name); return cast(O)this; } 97 98 // O attribute(this O)(string[string] values) { foreach(k, v; values) _attributes[k] = v; return cast(O)this; } 99 100 O accesskey(this O)(string value) { if (value.length > 0) attributes["accesskey"] = value; return cast(O)this; } 101 O contenteditable(this O)(bool value) { if (value) attributes["contenteditable"] = "true"; return cast(O)this; } 102 O contextmenu(this O)(string value) { if (value.length > 0) attributes["contextmenu"] = value; return cast(O)this; } 103 O dir(this O)(string value) { if (value.length > 0) attributes["dir"] = value; return cast(O)this; } 104 O draggable(this O)(bool value) { if (value) attributes["draggable"] = "true"; return cast(O)this; } 105 O dropzone(this O)(string value) { if (value.length > 0) attributes["dropzone"] = value; return cast(O)this; } 106 O hidden(this O)(bool value) { if (value) attributes["hidden"] = "true"; return cast(O)this; } 107 O lang(this O)(string value) { if (value.length > 0) attributes["lang"] = value; return cast(O)this; } 108 O spellcheck(this O)(bool value) { if (value) attributes["spellcheck"] = "true"; return cast(O)this; } 109 O style(this O)(string value) { if (value.length > 0) attributes["style"] = value; return cast(O)this; } 110 O tabindex(this O)(string value) { if (value.length > 0) attributes["tabindex"] = value; return cast(O)this; } 111 O title(this O)(string value) { if (value.length > 0) attributes["title"] = value; return cast(O)this; } 112 O translate(this O)(bool value) { if (value) attributes["translate"] = "true"; return cast(O)this; } 113 114 override bool opEquals(Object value) { return super.opEquals(value); } 115 116 bool opEquals(string html) { return toString == html; } 117 118 void opIndexAssign(T)(T value, string key) { _attributes[key] = "%s".format(value); } 119 void opIndexAssign(bool value, string key) { _attributes[key] = "%s".format((value) ? "true" : "false"); } 120 void opIndexAssign(T)(T value, string key, T notValue) { if (value != notValue) _attributes[key] = "%s".format(value); } 121 void opIndexAssign(bool value, string key, bool notValue) { if (value != notValue) _attributes[key] = "%s".format((value) ? "true" : "false"); } 122 123 string opBinary(string op)(string rhs) { 124 static if (op == "~") return toString ~ rhs; 125 else return null; 126 } 127 string opBinary(string op)(DH5Obj rhs) { 128 static if (op == "~") return toString ~ rhs.toString; 129 else return null; 130 } 131 132 O content(this O)(string addContent) { _html ~= H5String(addContent); return cast(O)this; } 133 O content(this O)(DH5Obj[] addContent...) { _html ~= addContent; return cast(O)this; } 134 O content(this O)(DH5Obj[] addContent) { _html ~= addContent; return cast(O)this; } 135 O content(this O)(DH5 addContent) { _html ~= addContent.objs; return cast(O)this; } 136 O clearContent(this O)() { _html = []; return cast(O)this; } 137 138 DCSSRules _css; 139 DCSSRules css() { return _css; } 140 O clearCss(this O)() { _css = CSSRules; return cast(O)this; } 141 O css(this O)(string aSelector, string name, string value) { return this.css(CSSRule(aSelector, name, value)); } 142 O css(this O)(string aSelector, string[string] someDeclarations) { return this.css(CSSRule(aSelector, someDeclarations)); } 143 O opCall(this O)(DCSSRule aRule) { return this.css(aRule); } 144 O opCall(this O)(DCSSRules aRules) { return this.css(aRules); } 145 O css(this O)(DCSSRule aRule) { _css(aRule); return cast(O)this; } 146 O css(this O)(DCSSRules aRules) { _css(aRules); return cast(O)this; } 147 unittest {} 148 149 O opCall(this O)(string[] someClasses) { add(someClasses); return cast(O)this; } 150 O opCall(this O)(string[string] someAttributes) { add(someAttributes); return cast(O)this; } 151 O opCall(this O)(string[] someContent...) { foreach(c; someContent) add(c); return cast(O)this; } 152 O opCall(this O)(DH5Obj[] someContent...) { add(someContent); return cast(O)this; } 153 O opCall(this O)(DH5Obj[] someContent) { add(someContent); return cast(O)this; } 154 O opCall(this O)(DH5 someContent) { add(someContent.objs); return cast(O)this; } 155 O opCall(this O)(DJS code) { this.js(code); return cast(O)this; } 156 unittest {} 157 158 O add(this O)(string[] someClasses) { _classes.add(someClasses); return cast(O)this; } 159 O add(this O)(string[string] someAttributes) { _attributes.add(someAttributes); return cast(O)this; } 160 O add(this O)(string someContent) { _html ~= H5String(someContent); return cast(O)this; } 161 O add(this O)(DH5Obj[] someContent...) { foreach(c; someContent) _html ~= c; return cast(O)this; } 162 O add(this O)(DH5Obj[] someContent) { foreach(c; someContent) _html ~= c; return cast(O)this; } 163 O add(this O)(DH5 someContent) { _html ~= someContent.objs; return cast(O)this; } 164 165 O clear(this O)() { 166 _css = CSSRules; 167 _html = []; 168 _js = ""; 169 return cast(O)this; 170 } 171 O clearHtml(this O)() { _html = []; return cast(O)this; } 172 O clearJs(this O)() { _js = ""; return cast(O)this; } 173 174 /* accesskey - specifies a shortcut key to activate/focus an element. */ 175 // mixin(H5Attribute("accesskey")); 176 // // className (class) - Specifies one or more classnames for an element (refers to a class in a style sheet) 177 // mixin(H5Attribute("className", "class")); 178 // 179 // // contenteditable - Specifies whether the content of an element is editable or not 180 // mixin(H5BoolAttribute("contenteditable")); 181 // 182 // // contextmenu - Specifies a context menu for an element. The context menu appears when a user right-clicks on the element 183 // mixin(H5Attribute("contextmenu")); 184 185 // data-* Used to store custom data private to the page or application 186 // dir Specifies the text direction for the content in an element 187 // draggable Specifies whether an element is draggable or not 188 // dropzone Specifies whether the dragged data is copied, moved, or linked, when dropped 189 // hidden Specifies that an element is not yet, or is no longer, relevant 190 // id Specifies a unique id for an element 191 // lang Specifies the language of the element's content 192 //spellcheck Specifies whether the element is to have its spelling and grammar checked or not 193 //style Specifies an inline CSS style for an element 194 //tabindex Specifies the tabbing order of an element 195 //title Specifies extra information about an element 196 //translate Specifies whether the content of an element should be translated or not 197 198 bool isBoolAttribute(string name) { 199 if (name in [ 200 "async":true, 201 "autocomplete":true, 202 "autofocus":true, 203 "autoplay":true, 204 "border":true, 205 "challenge":true, 206 "checked":true, 207 "compact":true, 208 "contenteditable":true, 209 "controls":true, 210 "default":true, 211 "defer":true, 212 "disabled":true, 213 "formNoValidate":true, 214 "frameborder":true, 215 "hidden":true, 216 "indeterminate":true, 217 "ismap":true, 218 "loop":true, 219 "multiple":true, 220 "muted":true, 221 "nohref":true, 222 "noresize":true, 223 "noshade":true, 224 "novalidate":true, 225 "nowrap":true, 226 "open":true, 227 "readonly":true, 228 "required":true, 229 "reversed":true, 230 "scoped":true, 231 "scrolling":true, 232 "seamless":true, 233 "selected":true, 234 "sortable":true, 235 "spellcheck":true, 236 "translate":true]) return true; 237 return false; 238 } 239 240 string attsToHTML() { 241 string[] items; 242 foreach(key; _attributes.keys.sort) { 243 switch(key.toLower) { 244 case "id": 245 this.id(_attributes[key]); 246 _attributes.remove(key); 247 break; 248 case "class": 249 this.classes(_attributes[key].split(" ")); 250 _attributes.remove(key); 251 break; 252 default: 253 if (isBoolAttribute(key)) items~=key; 254 else items~=key~`="`~_attributes[key]~`"`; 255 break; 256 } 257 } 258 return " "~items.join(" "); 259 } 260 261 string onlyCSS() { 262 if (auto result = _css.toString) return doubleTag("style", result); 263 return null; 264 } 265 string onlyHTML() { 266 string first; 267 string attsHTML = attsToHTML; 268 // firstTag 269 first ~= "<"~_tag; 270 271 if (_id.length > 0) first ~= ` id="`~_id~`"`; 272 273 if (_classes) { 274 string[] cls; 275 foreach(c; _classes.unique.sort) if (c.length > 0) cls ~= c.strip; 276 first ~= ` class="`~cls.join(" ")~`"`; 277 } 278 279 if (_attributes) first ~= attsHTML; 280 first ~= ">"; 281 if (_single) return first; 282 283 return first~_html.toString~endTag(_tag); 284 } 285 string onlyJS() { 286 if (_js.length > 0) return doubleTag("script", this.js); 287 return null; 288 } 289 override string toString() { 290 string result; 291 result ~= onlyCSS; 292 result ~= onlyHTML; 293 result ~= onlyJS; 294 return result; 295 } 296 297 string toJS(string target = null) { 298 return jsCreateElement(target, _tag, _classes, _attributes, _html.toString); // Not finish TODO 299 } 300 301 /// generate HTML in pretty format 302 string toPretty(int intendSpace = 0, int step = 2) { 303 string result; 304 result = startTag(this.tag, this.attributes).indent(intendSpace); 305 if (!single) { 306 result ~= "\n"; 307 foreach(obj; _html) result ~= obj.toPretty(intendSpace+step, step)~"\n"; 308 result ~= endTag(this.tag).indent(intendSpace); 309 } 310 return result; 311 } 312 unittest { 313 /* 314 writeln(H5Obj.tag("div").toPretty); 315 writeln("---------"); 316 writeln(H5Obj.tag("div")(H5Obj.tag("div")).toPretty); 317 writeln("---------"); 318 writeln(H5Obj.tag("div")(H5Obj.tag("div")(H5Obj.tag("div"))).toPretty); 319 */ 320 } 321 } 322 auto H5Obj(string content) { return new DH5Obj(content); } 323 auto H5Obj(DH5Obj[] content...) { return new DH5Obj(content); } 324 auto H5Obj(DH5Obj[] content) { return new DH5Obj(content); } 325 auto H5Obj(DH5 content) { return new DH5Obj(content); } 326 327 auto H5Obj(string id, string[] classes) { return new DH5Obj(id, classes); } 328 auto H5Obj(string id, string[] classes, string content) { return new DH5Obj(id, classes, content); } 329 auto H5Obj(string id, string[] classes, DH5Obj[] content...) { return new DH5Obj(id, classes, content); } 330 auto H5Obj(string id, string[] classes, DH5Obj[] content) { return new DH5Obj(id, classes, content); } 331 auto H5Obj(string id, string[] classes, DH5 content) { return new DH5Obj(id, classes, content); } 332 333 auto H5Obj(string id, string[string] attributes) { return new DH5Obj(id, attributes); } 334 auto H5Obj(string id, string[string] attributes, string content) { return new DH5Obj(id, attributes, content); } 335 auto H5Obj(string id, string[string] attributes, DH5Obj[] content...) { return new DH5Obj(id, attributes, content); } 336 auto H5Obj(string id, string[string] attributes, DH5Obj[] content) { return new DH5Obj(id, attributes, content); } 337 auto H5Obj(string id, string[string] attributes, DH5 content) { return new DH5Obj(id, attributes, content); } 338 339 auto H5Obj(string id, string[] classes, string[string] attributes) { return new DH5Obj(id, classes, attributes); } 340 auto H5Obj(string id, string[] classes, string[string] attributes, string content) { return new DH5Obj(id, classes, attributes, content); } 341 auto H5Obj(string id, string[] classes, string[string] attributes, DH5Obj[] content...) { return new DH5Obj(id, classes, attributes, content); } 342 auto H5Obj(string id, string[] classes, string[string] attributes, DH5Obj[] content) { return new DH5Obj(id, classes, attributes, content); } 343 auto H5Obj(string id, string[] classes, string[string] attributes, DH5 content) { return new DH5Obj(id, classes, attributes, content); } 344 345 auto H5Obj(string[] classes) { return new DH5Obj(classes); } 346 auto H5Obj(string[] classes, string content) { return new DH5Obj(classes, content); } 347 auto H5Obj(string[] classes, DH5Obj[] content...) { return new DH5Obj(classes, content); } 348 auto H5Obj(string[] classes, DH5Obj[] content) { return new DH5Obj(classes, content); } 349 auto H5Obj(string[] classes, DH5 content) { return new DH5Obj(classes, content); } 350 351 auto H5Obj(string[] classes, string[string] attributes) { return new DH5Obj(classes, attributes); } 352 auto H5Obj(string[] classes, string[string] attributes, string content) { return new DH5Obj(classes, attributes, content); } 353 auto H5Obj(string[] classes, string[string] attributes, DH5Obj[] content...) { return new DH5Obj(classes, attributes, content); } 354 auto H5Obj(string[] classes, string[string] attributes, DH5Obj[] content) { return new DH5Obj(classes, attributes, content); } 355 auto H5Obj(string[] classes, string[string] attributes, DH5 content) { return new DH5Obj(classes, attributes, content); } 356 357 auto H5Obj(string[string] attributes) { return new DH5Obj(attributes); } 358 auto H5Obj(string[string] attributes, string content) { return new DH5Obj(attributes, content); } 359 auto H5Obj(string[string] attributes, DH5Obj[] content...) { return new DH5Obj(attributes, content); } 360 auto H5Obj(string[string] attributes, DH5Obj[] content) { return new DH5Obj(attributes, content); } 361 auto H5Obj(string[string] attributes, DH5 content) { return new DH5Obj(attributes, content); } 362 363 unittest { 364 auto h5 = H5Obj; 365 assert(h5.id("newID").id == "newID"); 366 367 h5 = H5Obj("content"); 368 assert(H5Obj.id == null); 369 assert(H5Obj(["classA", "classB"]).id == null); 370 assert(H5Obj(["classA", "classB"], ["a":"x", "b":"y"]).id == null); 371 assert(H5Obj(["classA", "classB"], ["a":"x", "b":"y"], "content1").id == null); 372 assert(H5Obj(["a":"x", "b":"y"]).id == null); 373 assert(H5Obj(["a":"x", "b":"y"], "content1").id == null); 374 } 375 376 string toPretty(DH5Obj[] objs, int intendSpace = 0, int step = 2) { 377 string result; 378 foreach(obj; objs) { 379 if (obj) result ~= obj.toPretty; 380 } 381 return result; 382 }