-
Notifications
You must be signed in to change notification settings - Fork 2
/
jquery.maker.js
282 lines (233 loc) · 8.98 KB
/
jquery.maker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
(function ($, undefined) {
var guid = 1;
// All known HTML tag names, per http://dev.w3.org/html5/spec/Overview.html.
var tagNames = "a abbr address area article aside audio b base bdi bdo blockquote body br button canvas caption cite code col colgroup command data datalist dd del details dfn div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr i iframe img input ins kbd keygen label legend li link map mark menu meta meter nav noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr".split(" ");
// All known HTML attribute names, per http://dev.w3.org/html5/spec/Overview.html.
var attrNames = "accept accept-charset accesskey action alt async autocomplete autofocus autoplay border challenge charset checked cite class cols colspan content contenteditable contextmenu controls coords crossorigin data datetime default defer dir dirname disabled draggable dropzone enctype for form formaction formenctype formmethod formnovalidate formtarget headers height hidden high href hreflang http-equiv icon id ismap keytype kind label lang list loop low manifest max maxlength media mediagroup method min multiple muted name novalidate open optimum pattern placeholder poster preload radiogroup readonly rel required reversed rows rowspan sandbox scope scoped seamless selected shape size sizes span spellcheck src srcdoc srclang start step style tabindex target title type typemustmatch usemap value width wrap".split(" ");
// All known jQuery event names, per jQuery 1.7.
var eventNames = "blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" ");
var escapeMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": '''
};
/**
* Escapes all special HTML characters in the given `string`.
*/
function escapeHTML(string) {
if (string == null) {
return "";
}
return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
return escapeMap[s] || s;
});
}
var div = document.createElement("div");
/**
* Creates a DOM element from the given string of `html`.
*/
function makeDom(html) {
div.innerHTML = html;
return div.firstChild;
}
function makeMaker(tagName, attributes, callback) {
if (arguments.length === 1) {
attributes = {};
} else if (typeof attributes === "function") {
callback = attributes;
attributes = {};
}
var maker = new Maker(tagName, attributes);
if (typeof callback === "function") {
callback(maker);
}
return maker;
}
function Maker(tagName, attributes) {
if (typeof attributes === "string") {
attributes = {"class": attributes};
}
this.tagName = tagName || "div";
this.attributes = attributes || {};
this.children = [];
this.events = [];
}
/**
* Creates a new Maker, appends it as a child of this Maker, and returns
* this Maker. See makeMaker.
*/
Maker.prototype.make = function (tagName, attributes, callback) {
var maker = makeMaker(tagName, attributes, callback);
this.children.push(maker);
return maker;
};
/**
* Sets the attribute with the given `name` and returns this Maker.
*/
Maker.prototype.attr = function (name, value) {
this.attributes[name] = value;
return this;
};
/**
* Set inner text.
*/
Maker.prototype.text = function (value) {
this._text = value;
return this;
};
/**
* Set inner HTML.
*/
Maker.prototype.html = function (value) {
this._html = value;
return this;
};
/**
* Adds a class name to any previous value that may already exist and
* returns this Maker.
*/
Maker.prototype.addClass = function (value) {
var className = this.attributes["class"];
if (className) {
value = className + " " + value;
}
return this.attr("class", value);
};
/**
* Binds the event with the given `name` and returns this Maker.
*/
Maker.prototype.bind = function (name, data, handler) {
this.events.push(arguments);
return this;
};
/**
* Sugar for bind.
*/
Maker.prototype.on = Maker.prototype.bind;
var childMap = {
"ol": "li",
"ul": "li",
"table": "tr",
"thead": "tr",
"tbody": "tr",
"tr": "td",
"select": "option"
};
/**
* Used to easily create one child Maker for each element of the given
* `array`, the tagName of which is determined by this Maker's tagName. For
* example, if this Maker is a "ul", the child will be an "li". Likewise, a
* "select" yields "option" elements, etc.
*
* The callback is called with three arguments: 1) the item in the array, 2)
* the new child Maker object and 3) the index of the item in the original
* array. Returns this Maker.
*
* ul.map(items, function (item, li, index) {
* // ...
* });
*/
Maker.prototype.map = function (array, callback) {
var tagName = childMap[this.tagName] || "div";
for (var i = 0, len = array.length; i < len; ++i) {
callback(array[i], this.make(tagName), i);
}
return this;
};
/**
* Returns an array of tokens that represent the HTML markup of this Maker.
* All arguments are for internal use only.
*/
Maker.prototype.toMarkup = function (events, markup) {
markup = markup || [];
markup.push("<", this.tagName);
if (events && this.events.length) {
var id = this.attributes.id;
// Make sure this element has an id.
if (!id) {
id = this.attributes.id = "maker-" + guid++;
}
// Store the events for this element in the events map.
events[id] = this.events;
}
for (var name in this.attributes) {
markup.push(" ", name, '="', escapeHTML(this.attributes[name]), '"');
}
markup.push(">");
if (this._html) {
markup.push(this._html);
} else if (this._text) {
markup.push(escapeHTML(this._text));
}
for (var i = 0, len = this.children.length; i < len; ++i) {
this.children[i].toMarkup(events, markup);
}
markup.push("</", this.tagName, ">");
return markup;
};
/**
* Returns the HTML of this Maker. All arguments are for internal use only.
*/
Maker.prototype.toHTML = function (events) {
return this.toMarkup(events).join("");
};
/**
* Returns a jQuery object that represents this Maker, with all events
* already bound.
*/
Maker.prototype.toObj = function () {
var events = {};
var $obj = $(makeDom(this.toHTML(events)));
// Bind all events.
var $tmp, args;
for (var id in events) {
$tmp = $obj.find("#" + id);
args = events[id];
for (var i = 0, len = args.length; i < len; ++i) {
$tmp.bind.apply($tmp, args[i]);
}
}
return $obj;
};
for (var i = 0, len = attrNames.length; i < len; ++i) {
(function (attrName) {
Maker.prototype[attrName] = function (value) {
return this.attr(attrName, value);
}
})(attrNames[i]);
};
for (var i = 0, len = eventNames.length; i < len; ++i) {
(function (eventName) {
Maker.prototype[eventName] = function (data, handler) {
return this.bind(eventName, data, handler);
}
})(eventNames[i]);
};
for (var i = 0, len = tagNames.length; i < len; ++i) {
(function (tagName) {
Maker.prototype[tagName] = function (attributes, callback) {
return this.make(tagName, attributes, callback);
}
})(tagNames[i]);
};
// Expose.
$.Maker = Maker;
/**
* Creates a Maker with the given arguments, converts it to a jQuery object,
* and returns it.
*/
$.make = function (tagName, attributes, callback) {
return makeMaker(tagName, attributes, callback).toObj();
};
/**
* Creates a Maker with the given arguments and appends it to this jQuery
* object. Returns the newly created child jQuery object.
*/
$.fn.make = function (tagName, attributes, callback) {
var $obj = $.make(tagName, attributes, callback);
this.append($obj);
return $obj;
};
})(jQuery);