Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 591 lines (482 sloc) 17.987 kb
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
1 //
2 // KSHTMLWriter.m
3 //
9d91a67 @mikeabdullah MIT license
mikeabdullah authored
4 // Created by Mike Abdullah
5 // Copyright © 2010 Karelia Software
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
24 //
efecaa1 @mikeabdullah Settle on BSD license.
mikeabdullah authored
25
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
26 #import "KSHTMLWriter.h"
27
06fa83e @mikeabdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
28 #import "KSXMLAttributes.h"
3aeccf6 @mikeabdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
29
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
30
0225a1a @mikeabdullah Lots of lovely doctype constants
mikeabdullah authored
31 NSString *KSHTMLWriterDocTypeHTML_4_01_Strict = @"HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"";
32 NSString *KSHTMLWriterDocTypeHTML_4_01_Transitional = @"HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"";
33 NSString *KSHTMLWriterDocTypeHTML_4_01_Frameset = @"HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\"";
34 NSString *KSHTMLWriterDocTypeXHTML_1_0_Strict = @"html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"";
35 NSString *KSHTMLWriterDocTypeXHTML_1_0_Transitional = @"html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"";
36 NSString *KSHTMLWriterDocTypeXHTML_1_0_Frameset = @"html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\"";
37 NSString *KSHTMLWriterDocTypeXHTML_1_1 = @"html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"";
38 NSString *KSHTMLWriterDocTypeHTML_5 = @"html";
39
40
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
41 @interface KSHTMLWriter ()
42 @property(nonatomic, copy, readwrite) NSString *docType;
43 @end
44
45
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
46 @implementation KSHTMLWriter
47
72adfa1 @mikeabdullah Documenting .isXHTML generation better.
mikeabdullah authored
48 #pragma mark Creating an HTML Writer
49
5f5a663 @mikeabdullah Consistently use "writer" or "output" terminology.
mikeabdullah authored
50 - (id)initWithOutputWriter:(id <KSWriter>)output;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
51 {
79d7eb1 @mikeabdullah proper init method style
mikeabdullah authored
52 if (self = [super initWithOutputWriter:output])
53 {
54 [self setDocType:KSHTMLWriterDocTypeHTML_5];
55 _IDs = [[NSMutableSet alloc] init];
56 _classNames = [[NSMutableArray alloc] init];
57 }
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
58
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
59 return self;
60 }
61
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
62 - (id)initWithOutputWriter:(id <KSWriter>)output docType:(NSString *)docType encoding:(NSStringEncoding)encoding;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
63 {
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
64 if (self = [self initWithOutputWriter:output encoding:encoding])
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
65 {
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
66 [self setDocType:docType];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
67 }
68
69 return self;
70 }
71
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
72 - (void)dealloc
73 {
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
74 [_IDs release];
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
75 [_classNames release];
76
77 [super dealloc];
78 }
79
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
80 #pragma mark DTD
72adfa1 @mikeabdullah Documenting .isXHTML generation better.
mikeabdullah authored
81
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
82 - (void)startDocumentWithDocType:(NSString *)docType encoding:(NSStringEncoding)encoding;
83 {
84 [self setDocType:docType];
85 [super startDocumentWithDocType:docType encoding:encoding];
86 }
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
87
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
88 @synthesize docType = _docType;
89 - (void)setDocType:(NSString *)docType;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
90 {
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
91 docType = [docType copy];
92 [_docType release]; _docType = docType;
93
3e35588 @mikeabdullah Convenience class method to know if a doctype will produce XHTML
mikeabdullah authored
94 _isXHTML = [[self class] isDocTypeXHTML:docType];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
95 }
96
265ea17 @mikeabdullah Make use of the new docType support
mikeabdullah authored
97 - (BOOL)isXHTML; { return _isXHTML; }
98
3e35588 @mikeabdullah Convenience class method to know if a doctype will produce XHTML
mikeabdullah authored
99 + (BOOL)isDocTypeXHTML:(NSString *)docType;
100 {
101 BOOL result = !([docType isEqualToString:KSHTMLWriterDocTypeHTML_4_01_Strict] ||
102 [docType isEqualToString:KSHTMLWriterDocTypeHTML_4_01_Transitional] ||
103 [docType isEqualToString:KSHTMLWriterDocTypeHTML_4_01_Frameset]);
104 return result;
105 }
106
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
107 #pragma mark CSS Class Name
108
5df9cd7 @mikeabdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
109 - (NSString *)currentElementClassName;
ecfabe0 @mikeabdullah Expose -className.
mikeabdullah authored
110 {
111 NSString *result = nil;
112 if ([_classNames count])
113 {
114 result = [_classNames componentsJoinedByString:@" "];
115 }
116 return result;
117 }
118
5df9cd7 @mikeabdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
119 - (void)pushClassName:(NSString *)className;
120 {
98928dc @mikeabdullah Log if trying to add a CSS class twice to an element during debug builds...
mikeabdullah authored
121 #ifdef DEBUG
122 if ([_classNames containsObject:className])
123 {
124 NSLog(@"Adding class \"%@\" to an element twice", className);
125 }
126 #endif
127
5df9cd7 @mikeabdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
128 [_classNames addObject:className];
129 }
130
ad2cbc6 @mikeabdullah -pushClassNames:
mikeabdullah authored
131 - (void)pushClassNames:(NSArray *)classNames;
132 {
133 // TODO: Check for duplicates while debugging
134 [_classNames addObjectsFromArray:classNames];
135 }
136
30bbf6a Allow values for attributes to be type id, not
Dan Wood authored
137 - (void)pushAttribute:(NSString *)attribute value:(id)value;
e96c6c8 @mikeabdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
138 {
139 if ([attribute isEqualToString:@"class"])
140 {
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
141 return [self pushClassName:value];
e96c6c8 @mikeabdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
142 }
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
143
144 // Keep track of IDs in use
145 if ([attribute isEqualToString:@"id"]) [_IDs addObject:value];
146 [super pushAttribute:attribute value:value];
e96c6c8 @mikeabdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
147 }
148
06fa83e @mikeabdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
149 - (KSXMLAttributes *)currentAttributes;
589179c @mikeabdullah Include -className in -elementAttributes.
mikeabdullah authored
150 {
06fa83e @mikeabdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
151 KSXMLAttributes *result = [super currentAttributes];
589179c @mikeabdullah Include -className in -elementAttributes.
mikeabdullah authored
152
3aeccf6 @mikeabdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
153 // Add in buffered class info
5df9cd7 @mikeabdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
154 NSString *class = [self currentElementClassName];
3aeccf6 @mikeabdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
155 if (class) [result addAttribute:@"class" value:class];
589179c @mikeabdullah Include -className in -elementAttributes.
mikeabdullah authored
156
157 return result;
158 }
159
0e37440 @mikeabdullah Merge branch 'release-1.2'
mikeabdullah authored
160 - (BOOL)hasCurrentAttributes;
d7376e3 @mikeabdullah Sister method: -[KSXMLWriter currentElementHasAttributes]
mikeabdullah authored
161 {
0e37440 @mikeabdullah Merge branch 'release-1.2'
mikeabdullah authored
162 return ([super hasCurrentAttributes] || [_classNames count]);
d7376e3 @mikeabdullah Sister method: -[KSXMLWriter currentElementHasAttributes]
mikeabdullah authored
163 }
164
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
165 #pragma mark HTML Fragments
166
19439d3 @mikeabdullah -writeHTMLString:withTerminatingNewline:
mikeabdullah authored
167 - (void)writeHTMLString:(NSString *)html withTerminatingNewline:(BOOL)terminatingNewline;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
168 {
19439d3 @mikeabdullah -writeHTMLString:withTerminatingNewline:
mikeabdullah authored
169 if (terminatingNewline)
170 {
171 if (![html hasSuffix:@"\n"]) html = [html stringByAppendingString:@"\n"];
172 }
173 else
174 {
175 if ([html hasSuffix:@"\n"]) html = [html substringToIndex:[html length] - 1];
176 }
177
178 [self writeHTMLString:html];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
179 }
180
181 - (void)writeHTMLString:(NSString *)html;
182 {
6bfd659 @mikeabdullah Account for indentation level in -writeHTMLString:
mikeabdullah authored
183 NSUInteger indent = [self indentationLevel];
184 if (indent)
185 {
186 NSString *indentedNewline = [@"\n" stringByPaddingToLength:indent + 1
187 withString:@"\t"
188 startingAtIndex:0];
189
190 html = [html stringByReplacingOccurrencesOfString:@"\n" withString:indentedNewline];
191 }
192
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
193 [self writeString:html];
194 }
195
196 #pragma mark General
197
bc8ed57 @mikeabdullah -writeElement:idName:className:content: convenience.
mikeabdullah authored
198 - (void)writeElement:(NSString *)name idName:(NSString *)idName className:(NSString *)className content:(void (^)(void))content;
199 {
200 if (idName) [self pushAttribute:@"id" value:idName];
201 if (className) [self pushAttribute:@"class" value:className];
202
203 [self writeElement:name content:content];
204 }
205
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
206 - (void)startElement:(NSString *)tagName className:(NSString *)className;
207 {
208 [self startElement:tagName idName:nil className:className];
209 }
210
211 - (void)startElement:(NSString *)tagName idName:(NSString *)idName className:(NSString *)className;
212 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
213 if (idName) [self pushAttribute:@"id" value:idName];
214 if (className) [self pushAttribute:@"class" value:className];
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
215
216 [self startElement:tagName];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
217 }
218
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
219 - (BOOL)isIDValid:(NSString *)anID; // NO if the ID has already been used
220 {
221 BOOL result = ![_IDs containsObject:anID];
222 return result;
223 }
224
73124a4 @mikeabdullah Convenience method for writing standard HTML document structure.
mikeabdullah authored
225 #pragma mark Document
226
227 - (void)writeDocumentOfType:(NSString *)docType encoding:(NSStringEncoding)encoding head:(void (^)(void))headBlock body:(void (^)(void))bodyBlock;
228 {
229 [self startDocumentWithDocType:docType encoding:encoding];
230
231 [self writeElement:@"html" content:^{
232 if (headBlock) [self writeElement:@"head" content:headBlock];
233 [self writeElement:@"body" content:bodyBlock];
234 }];
235 }
236
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
237 #pragma mark Line Break
238
239 - (void)writeLineBreak;
240 {
241 [self startElement:@"br"];
242 [self endElement];
243 }
244
62da5a7 @mikeabdullah Block-based API for writing anchor elements.
mikeabdullah authored
245 #pragma mark Anchors
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
246
247 - (void)startAnchorElementWithHref:(NSString *)href title:(NSString *)titleString target:(NSString *)targetString rel:(NSString *)relString;
248 {
62da5a7 @mikeabdullah Block-based API for writing anchor elements.
mikeabdullah authored
249 // TODO: Remove this method once Sandvox no longer needs it
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
250 if (href) [self pushAttribute:@"href" value:href];
251 if (targetString) [self pushAttribute:@"target" value:targetString];
252 if (titleString) [self pushAttribute:@"title" value:titleString];
253 if (relString) [self pushAttribute:@"rel" value:relString];
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
254
255 [self startElement:@"a"];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
256 }
257
62da5a7 @mikeabdullah Block-based API for writing anchor elements.
mikeabdullah authored
258 - (void)writeAnchorElementWithHref:(NSString *)href title:(NSString *)titleString target:(NSString *)targetString rel:(NSString *)relString content:(void (^)(void))content;
259 {
badc8ec @mikeabdullah Assert against nil content block when appropriate.
mikeabdullah authored
260 NSParameterAssert(content);
261
62da5a7 @mikeabdullah Block-based API for writing anchor elements.
mikeabdullah authored
262 [self startAnchorElementWithHref:href title:titleString target:targetString rel:relString];
263 content();
264 [self endElement];
265 }
266
267 #pragma mark Images
268
ac684bc @mikeabdullah Don't need to pass id and class directly when writing an image.
mikeabdullah authored
269 - (void)writeImageWithSrc:(NSString *)src
270 alt:(NSString *)alt
30bbf6a Allow values for attributes to be type id, not
Dan Wood authored
271 width:(id)width
272 height:(id)height;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
273 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
274 [self pushAttribute:@"src" value:src];
275 [self pushAttribute:@"alt" value:alt];
276 if (width) [self pushAttribute:@"width" value:width];
277 if (height) [self pushAttribute:@"height" value:height];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
278
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
279 [self startElement:@"img"];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
280 [self endElement];
281 }
282
283 #pragma mark Link
284
285 - (void)writeLinkWithHref:(NSString *)href
286 type:(NSString *)type
287 rel:(NSString *)rel
288 title:(NSString *)title
289 media:(NSString *)media;
290 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
291 if (rel) [self pushAttribute:@"rel" value:rel];
e982500 @mikeabdullah <LINK>s don't have text/css as default type any more because some links ...
mikeabdullah authored
292 if (type) [self pushAttribute:@"type" value:type];
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
293 [self pushAttribute:@"href" value:href];
294 if (title) [self pushAttribute:@"title" value:title];
295 if (media) [self pushAttribute:@"media" value:media];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
296
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
297 [self startElement:@"link"];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
298 [self endElement];
299 }
300
301 - (void)writeLinkToStylesheet:(NSString *)href
302 title:(NSString *)title
303 media:(NSString *)media;
304 {
e982500 @mikeabdullah <LINK>s don't have text/css as default type any more because some links ...
mikeabdullah authored
305 [self writeLinkWithHref:href type:@"text/css" rel:@"stylesheet" title:title media:media];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
306 }
307
60c7e89 @mikeabdullah Simpler method for pulling in an external script.
mikeabdullah authored
308 #pragma mark Scripts
309
3a9930c @mikeabdullah -writeJavascriptWithSrc:encoding: method that tries to do the right with...
mikeabdullah authored
310 - (void)writeJavascriptWithSrc:(NSString *)src encoding:(NSStringEncoding)encoding;
311 {
312 // According to the HTML spec, charset only needs to be specified if the script is a different encoding to the document
313 NSString *charset = nil;
314 if (encoding != [self encoding])
315 {
316 charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(encoding));
317 }
318
319 [self writeJavascriptWithSrc:src charset:charset];
320 }
321
b695665 Src may be nil; it's handled in support classes
Dan Wood authored
322 - (void)writeJavascriptWithSrc:(NSString *)src charset:(NSString *)charset; // src may be nil
323 {
1d5ee5f @mikeabdullah If linking to a script, generally should specify charset
mikeabdullah authored
324 if (charset) [self pushAttribute:@"charset" value:charset];
1ca811e @mikeabdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
325 [self startJavascriptElementWithSrc:src];
60c7e89 @mikeabdullah Simpler method for pulling in an external script.
mikeabdullah authored
326 [self endElement];
327 }
de567f7 added method to output JavaScript (via Dan)
Terrence Talbot authored
328
4d9ca24 @mikeabdullah -writeJavascript:useCDATA: convenience method.
mikeabdullah authored
329 - (void)writeJavascript:(NSString *)script useCDATA:(BOOL)useCDATA;
330 {
3bcec24 @mikeabdullah -writeJavascriptWithContent:
mikeabdullah authored
331 [self writeJavascriptWithContent:^{
332
faeb8e5 @mikeabdullah Align javascripts with their <SCRIPT> tag.
mikeabdullah authored
333 if (useCDATA) [self startJavascriptCDATA];
334 [self writeHTMLString:script];
335 if (useCDATA) [self endJavascriptCDATA];
3bcec24 @mikeabdullah -writeJavascriptWithContent:
mikeabdullah authored
336 }];
337 }
338
339 - (void)writeJavascriptWithContent:(void (^)(void))content;
340 {
341 [self startJavascriptElementWithSrc:nil];
342 content();
343 [self increaseIndentationLevel]; // compensate for -decreaseIndentationLevel
4d9ca24 @mikeabdullah -writeJavascript:useCDATA: convenience method.
mikeabdullah authored
344 [self endElement];
345 }
346
1ca811e @mikeabdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
347 - (void)startJavascriptElementWithSrc:(NSString *)src; // src may be nil
348 {
4af9cd9 @mikeabdullah HTML5 doesn't bother specifying the type of <SCRIPT>s
mikeabdullah authored
349 // HTML5 doesn't need the script type specified, but older doc types do for standards-compliance
350 if (![[self docType] isEqualToString:KSHTMLWriterDocTypeHTML_5])
351 {
352 [self pushAttribute:@"type" value:@"text/javascript"];
353 }
354
355 // Script
3530727 Don't specify charset for inline javascript, only
Dan Wood authored
356 if (src)
357 {
358 [self pushAttribute:@"src" value:src];
12b9584 @mikeabdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
359 [self startElement:@"script"];
3530727 Don't specify charset for inline javascript, only
Dan Wood authored
360 }
12b9584 @mikeabdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
361 else
2d68556 @mikeabdullah Embedded scripts should also write their end tag on a new line.
mikeabdullah authored
362 {
12b9584 @mikeabdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
363 // Embedded scripts should start on their own line for clarity
5d1fcbe @mikeabdullah typo
mikeabdullah authored
364 // Outdent the script comapred to what's normal
12b9584 @mikeabdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
365 [self startElement:@"script" writeInline:NO];
366
367 if (!src)
368 {
faeb8e5 @mikeabdullah Align javascripts with their <SCRIPT> tag.
mikeabdullah authored
369 [self decreaseIndentationLevel];
a84c9d5 @mikeabdullah Nicely apply indentation to scripts.
mikeabdullah authored
370 [self startNewline];
12b9584 @mikeabdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
371 [self stopWritingInline];
372 }
2d68556 @mikeabdullah Embedded scripts should also write their end tag on a new line.
mikeabdullah authored
373 }
1ca811e @mikeabdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
374 }
375
f241869 @mikeabdullah Split out -startJavascriptCDATA and -endJavascriptCDATA.
mikeabdullah authored
376 - (void)startJavascriptCDATA;
377 {
378 [self writeString:@"\n/* "];
379 [self startCDATA];
380 [self writeString:@" */"];
381 }
382
383 - (void)endJavascriptCDATA;
384 {
385 [self writeString:@"\n/* "];
386 [self endCDATA];
387 [self writeString:@" */\n"];
388 }
389
0bc7e19 Add new convenience method for writing <param name="foo" value="bar" />
Dan Wood authored
390 #pragma mark Param
391
392 - (void)writeParamElementWithName:(NSString *)name value:(NSString *)value;
393 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
394 if (name) [self pushAttribute:@"name" value:name];
395 if (value) [self pushAttribute:@"value" value:value];
0bc7e19 Add new convenience method for writing <param name="foo" value="bar" />
Dan Wood authored
396 [self startElement:@"param"];
397 [self endElement];
398 }
399
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
400 #pragma mark Style
401
6dd07ee @mikeabdullah Convenience method for writing inline CSS.
mikeabdullah authored
402 - (void)writeStyleElementWithCSSString:(NSString *)css;
403 {
404 [self startStyleElementWithType:@"text/css"];
405 [self writeString:css]; // browsers don't expect styles to be XML escaped
406 [self endElement];
407 }
408
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
409 - (void)startStyleElementWithType:(NSString *)type;
410 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
411 if (type) [self pushAttribute:@"type" value:type];
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
412 [self startElement:@"style"];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
413 }
414
415 #pragma mark Elements Stack
416
1b3d197 @mikeabdullah KSHTMLWriter gains:
mikeabdullah authored
417 - (BOOL)hasListOpen;
418 {
419 return ([self hasOpenElement:@"ul"] || [self hasOpenElement:@"ol"]);
420 }
421
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
422 - (BOOL)topElementIsList;
423 {
1b3d197 @mikeabdullah KSHTMLWriter gains:
mikeabdullah authored
424 return [[self class] elementIsList:[self topElement]];
425 }
426
427 + (BOOL)elementIsList:(NSString *)element;
428 {
429 BOOL result = ([element isEqualToString:@"ul"] ||
430 [element isEqualToString:@"ol"]);
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
431 return result;
432 }
433
c35c721 @mikeabdullah Only a handful of HTML elements are allowed to be empty.
mikeabdullah authored
434 #pragma mark (X)HTML
435
436 - (BOOL)elementCanBeEmpty:(NSString *)tagName;
437 {
8c20ada @mikeabdullah Improve performance.
mikeabdullah authored
438 static NSSet *emptyTags;
439 static dispatch_once_t onceToken;
440 dispatch_once(&onceToken, ^{
441
442 emptyTags = [[NSSet alloc] initWithObjects:
443 @"br",
444 @"img",
445 @"hr",
446 @"meta",
447 @"link",
448 @"input",
449 @"base",
450 @"basefont",
451 @"param",
452 @"area",
453 @"source", nil];
454 });
c35c721 @mikeabdullah Only a handful of HTML elements are allowed to be empty.
mikeabdullah authored
455
8c20ada @mikeabdullah Improve performance.
mikeabdullah authored
456 return [emptyTags containsObject:tagName];
c35c721 @mikeabdullah Only a handful of HTML elements are allowed to be empty.
mikeabdullah authored
457 }
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
458
89aba47 @mikeabdullah Refactor inline writing decisions across an instance & class method.
mikeabdullah authored
459 + (BOOL)shouldPrettyPrintElementInline:(NSString *)elementName;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
460 {
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
461 switch ([elementName length])
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
462 {
463 case 1:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
464 if ([elementName isEqualToString:@"a"] ||
465 [elementName isEqualToString:@"b"] ||
466 [elementName isEqualToString:@"i"] ||
d70ebbd @mikeabdullah <STRIKE> and <S> can be written inline.
mikeabdullah authored
467 [elementName isEqualToString:@"s"] ||
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
468 [elementName isEqualToString:@"u"] ||
469 [elementName isEqualToString:@"q"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
470 break;
471
472 case 2:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
473 if ([elementName isEqualToString:@"br"] ||
474 [elementName isEqualToString:@"em"] ||
475 [elementName isEqualToString:@"tt"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
476 break;
477
478 case 3:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
479 if ([elementName isEqualToString:@"img"] ||
480 [elementName isEqualToString:@"sup"] ||
481 [elementName isEqualToString:@"sub"] ||
482 [elementName isEqualToString:@"big"] ||
483 [elementName isEqualToString:@"del"] ||
484 [elementName isEqualToString:@"ins"] ||
485 [elementName isEqualToString:@"dfn"] ||
486 [elementName isEqualToString:@"map"] ||
487 [elementName isEqualToString:@"var"] ||
488 [elementName isEqualToString:@"bdo"] ||
489 [elementName isEqualToString:@"kbd"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
490 break;
491
492 case 4:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
493 if ([elementName isEqualToString:@"span"] ||
494 [elementName isEqualToString:@"font"] ||
495 [elementName isEqualToString:@"abbr"] ||
496 [elementName isEqualToString:@"cite"] ||
497 [elementName isEqualToString:@"code"] ||
498 [elementName isEqualToString:@"samp"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
499 break;
500
501 case 5:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
502 if ([elementName isEqualToString:@"small"] ||
503 [elementName isEqualToString:@"input"] ||
504 [elementName isEqualToString:@"label"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
505 break;
506
507 case 6:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
508 if ([elementName isEqualToString:@"strong"] ||
509 [elementName isEqualToString:@"select"] ||
510 [elementName isEqualToString:@"button"] ||
511 [elementName isEqualToString:@"object"] ||
512 [elementName isEqualToString:@"applet"] ||
d70ebbd @mikeabdullah <STRIKE> and <S> can be written inline.
mikeabdullah authored
513 [elementName isEqualToString:@"script"] ||
514 [elementName isEqualToString:@"strike"]) return YES;
782c637 @mikeabdullah Expand the list of known inline elements
mikeabdullah authored
515 break;
516
517 case 7:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
518 if ([elementName isEqualToString:@"acronym"]) return YES;
782c637 @mikeabdullah Expand the list of known inline elements
mikeabdullah authored
519 break;
520
521 case 8:
d1e15e8 @mikeabdullah correct terminology
mikeabdullah authored
522 if ([elementName isEqualToString:@"textarea"]) return YES;
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
523 break;
524 }
525
89aba47 @mikeabdullah Refactor inline writing decisions across an instance & class method.
mikeabdullah authored
526 return [super shouldPrettyPrintElementInline:elementName];
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
527 }
528
05b69fd @mikeabdullah HTML Writer reports that lists may only contain list items.
mikeabdullah authored
529 - (BOOL)validateElement:(NSString *)element;
530 {
531 if (![super validateElement:element]) return NO;
532
533 // Lists can only contain list items
534 if ([self topElementIsList])
535 {
536 return [element isEqualToString:@"li"];
537 }
538 else
539 {
540 return YES;
541 }
542 }
543
a85616d @mikeabdullah Only ordered list items can have a value.
mikeabdullah authored
544 - (NSString *)validateAttribute:(NSString *)name value:(NSString *)value ofElement:(NSString *)element;
545 {
546 NSString *result = [super validateAttribute:name value:value ofElement:element];
547 if (!result) return nil;
548
549 // value is only allowed as a list item attribute when in an ordered list
550 if ([element isEqualToString:@"li"] && [name isEqualToString:@"value"])
551 {
552 if (![[self topElement] isEqualToString:@"ol"]) result = nil;
553 }
554
555 return result;
556 }
557
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
558 #pragma mark Element Primitives
559
c8bc317 @mikeabdullah Can now ditch -openTag:writeInline:
mikeabdullah authored
560 - (void)startElement:(NSString *)elementName writeInline:(BOOL)writeInline; // for more control
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
561 {
1fa7ee9 @mikeabdullah Checking HTML tags is a waste of time outside of debug builds I'd judge.
mikeabdullah authored
562 #ifdef DEBUG
566d24c @mikeabdullah non-lowercase tags shouldn't be used for HTML.
mikeabdullah authored
563 NSAssert1([elementName isEqualToString:[elementName lowercaseString]], @"Attempt to start non-lowercase element: %@", elementName);
1fa7ee9 @mikeabdullah Checking HTML tags is a waste of time outside of debug builds I'd judge.
mikeabdullah authored
564 #endif
566d24c @mikeabdullah non-lowercase tags shouldn't be used for HTML.
mikeabdullah authored
565
566
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
567 // Add in any pre-written classes
5df9cd7 @mikeabdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
568 NSString *class = [self currentElementClassName];
ecfabe0 @mikeabdullah Expose -className.
mikeabdullah authored
569 if (class)
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
570 {
571 [_classNames removeAllObjects];
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
572 [super pushAttribute:@"class" value:class];
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
573 }
9beb73b @mikeabdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
574
c8bc317 @mikeabdullah Can now ditch -openTag:writeInline:
mikeabdullah authored
575 [super startElement:elementName writeInline:writeInline];
75c4ca6 @mikeabdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
576 }
577
994c91e @mikeabdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
578 - (void)closeEmptyElementTag; // /> OR > depending on -isXHTML
579 {
580 if ([self isXHTML])
581 {
582 [super closeEmptyElementTag];
583 }
584 else
585 {
586 [self writeString:@">"];
587 }
588 }
589
590 @end
Something went wrong with that request. Please try again.