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 }