Skip to content

HTTPS clone URL

Subversion checkout URL

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