Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 588 lines (479 sloc) 18.138 kb
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
1 //
2 // KSHTMLWriter.m
3 //
ac8ae26 Dan Wood update copyrights to 2012
danwood authored
4 // Copyright 2010-2012, Mike Abdullah and Karelia Software
efecaa1 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
25 //
26
efecaa1 Mike Abdullah Settle on BSD license.
mikeabdullah authored
27
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
28 #import "KSHTMLWriter.h"
29
06fa83e Mike Abdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
30 #import "KSXMLAttributes.h"
3aeccf6 Mike Abdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
31
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
32
0225a1a Mike Abdullah 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 Mike Abdullah Make use of the new docType support
mikeabdullah authored
43 @interface KSHTMLWriter ()
44 @property(nonatomic, copy, readwrite) NSString *docType;
45 @end
46
47
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
48 @implementation KSHTMLWriter
49
72adfa1 Mike Abdullah Documenting .isXHTML generation better.
mikeabdullah authored
50 #pragma mark Creating an HTML Writer
51
5f5a663 Mike Abdullah Consistently use "writer" or "output" terminology.
mikeabdullah authored
52 - (id)initWithOutputWriter:(id <KSWriter>)output;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
53 {
79d7eb1 Mike Abdullah 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 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
60
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
61 return self;
62 }
63
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
64 - (id)initWithOutputWriter:(id <KSWriter>)output docType:(NSString *)docType encoding:(NSStringEncoding)encoding;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
65 {
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
66 if (self = [self initWithOutputWriter:output encoding:encoding])
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
67 {
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
68 [self setDocType:docType];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
69 }
70
71 return self;
72 }
73
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
74 - (void)dealloc
75 {
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
76 [_IDs release];
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
77 [_classNames release];
78
79 [super dealloc];
80 }
81
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
82 #pragma mark DTD
72adfa1 Mike Abdullah Documenting .isXHTML generation better.
mikeabdullah authored
83
265ea17 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
89
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
90 @synthesize docType = _docType;
91 - (void)setDocType:(NSString *)docType;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
92 {
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
93 docType = [docType copy];
94 [_docType release]; _docType = docType;
95
3e35588 Mike Abdullah Convenience class method to know if a doctype will produce XHTML
mikeabdullah authored
96 _isXHTML = [[self class] isDocTypeXHTML:docType];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
97 }
98
265ea17 Mike Abdullah Make use of the new docType support
mikeabdullah authored
99 - (BOOL)isXHTML; { return _isXHTML; }
100
3e35588 Mike Abdullah 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 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
109 #pragma mark CSS Class Name
110
5df9cd7 Mike Abdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
111 - (NSString *)currentElementClassName;
ecfabe0 Mike Abdullah 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 Mike Abdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
121 - (void)pushClassName:(NSString *)className;
122 {
98928dc Mike Abdullah Log if trying to add a CSS class twice to an element during debug builds...
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 Mike Abdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
130 [_classNames addObject:className];
131 }
132
ad2cbc6 Mike Abdullah -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 Mike Abdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
140 {
141 if ([attribute isEqualToString:@"class"])
142 {
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
Mike authored
143 return [self pushClassName:value];
e96c6c8 Mike Abdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
144 }
fba7c04 HTML Writer tracks all element IDs written, so can tell you if an ID is ...
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 Mike Abdullah Calling [htmlWriter pushElementAttribute:@"class" value:@"foo"] automati...
mikeabdullah authored
149 }
150
06fa83e Mike Abdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
151 - (KSXMLAttributes *)currentAttributes;
589179c Mike Abdullah Include -className in -elementAttributes.
mikeabdullah authored
152 {
06fa83e Mike Abdullah On reflection, it makes more sense to represent only the attributes of a...
mikeabdullah authored
153 KSXMLAttributes *result = [super currentAttributes];
589179c Mike Abdullah Include -className in -elementAttributes.
mikeabdullah authored
154
3aeccf6 Mike Abdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
155 // Add in buffered class info
5df9cd7 Mike Abdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
156 NSString *class = [self currentElementClassName];
3aeccf6 Mike Abdullah KSXMLWriter uses a KSElementInfo object internally.
mikeabdullah authored
157 if (class) [result addAttribute:@"class" value:class];
589179c Mike Abdullah Include -className in -elementAttributes.
mikeabdullah authored
158
159 return result;
160 }
161
0e37440 Mike Abdullah Merge branch 'release-1.2'
mikeabdullah authored
162 - (BOOL)hasCurrentAttributes;
d7376e3 Mike Abdullah Sister method: -[KSXMLWriter currentElementHasAttributes]
mikeabdullah authored
163 {
0e37440 Mike Abdullah Merge branch 'release-1.2'
mikeabdullah authored
164 return ([super hasCurrentAttributes] || [_classNames count]);
d7376e3 Mike Abdullah Sister method: -[KSXMLWriter currentElementHasAttributes]
mikeabdullah authored
165 }
166
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
167 #pragma mark HTML Fragments
168
19439d3 Mike Abdullah -writeHTMLString:withTerminatingNewline:
mikeabdullah authored
169 - (void)writeHTMLString:(NSString *)html withTerminatingNewline:(BOOL)terminatingNewline;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
170 {
19439d3 Mike Abdullah -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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
181 }
182
183 - (void)writeHTMLString:(NSString *)html;
184 {
6bfd659 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
195 [self writeString:html];
196 }
197
198 #pragma mark General
199
bc8ed57 Mike Abdullah -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 Mike Abdullah 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 wo...
Mike authored
215 if (idName) [self pushAttribute:@"id" value:idName];
216 if (className) [self pushAttribute:@"class" value:className];
9beb73b Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
217
218 [self startElement:tagName];
994c91e Mike Abdullah 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 is ...
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 Mike Abdullah 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 Mike Abdullah 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 Mike Abdullah Block-based API for writing anchor elements.
mikeabdullah authored
247 #pragma mark Anchors
994c91e Mike Abdullah 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 Mike Abdullah 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 wo...
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 Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
256
257 [self startElement:@"a"];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
258 }
259
62da5a7 Mike Abdullah 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 Mike Abdullah Assert against nil content block when appropriate.
mikeabdullah authored
262 NSParameterAssert(content);
263
62da5a7 Mike Abdullah 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 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
275 {
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
280
9beb73b Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
281 [self startElement:@"img"];
994c91e Mike Abdullah 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 wo...
Mike authored
293 if (rel) [self pushAttribute:@"rel" value:rel];
e982500 Mike Abdullah <LINK>s don't have text/css as default type any more because some links ...
mikeabdullah authored
294 if (type) [self pushAttribute:@"type" value:type];
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
298
9beb73b Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
299 [self startElement:@"link"];
994c91e Mike Abdullah 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 Mike Abdullah <LINK>s don't have text/css as default type any more because some links ...
mikeabdullah authored
307 [self writeLinkWithHref:href type:@"text/css" rel:@"stylesheet" title:title media:media];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
308 }
309
60c7e89 Mike Abdullah Simpler method for pulling in an external script.
mikeabdullah authored
310 #pragma mark Scripts
311
3a9930c Mike Abdullah -writeJavascriptWithSrc:encoding: method that tries to do the right with...
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 Mike Abdullah If linking to a script, generally should specify charset
mikeabdullah authored
326 if (charset) [self pushAttribute:@"charset" value:charset];
1ca811e Mike Abdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
327 [self startJavascriptElementWithSrc:src];
60c7e89 Mike Abdullah 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 Mike Abdullah -writeJavascript:useCDATA: convenience method.
mikeabdullah authored
331 - (void)writeJavascript:(NSString *)script useCDATA:(BOOL)useCDATA;
332 {
1ca811e Mike Abdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
333 [self startJavascriptElementWithSrc:nil];
faeb8e5 Mike Abdullah 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 Mike Abdullah -writeJavascript:useCDATA: convenience method.
mikeabdullah authored
341 [self endElement];
342 }
343
1ca811e Mike Abdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
344 - (void)startJavascriptElementWithSrc:(NSString *)src; // src may be nil
345 {
4af9cd9 Mike Abdullah 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 Mike Abdullah 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 Mike Abdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
358 else
2d68556 Mike Abdullah Embedded scripts should also write their end tag on a new line.
mikeabdullah authored
359 {
12b9584 Mike Abdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
360 // Embedded scripts should start on their own line for clarity
faeb8e5 Mike Abdullah Align javascripts with their <SCRIPT> tag.
mikeabdullah authored
361 // Outdent the script comapred to wha'ts normal
12b9584 Mike Abdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
362 [self startElement:@"script" writeInline:NO];
363
364 if (!src)
365 {
faeb8e5 Mike Abdullah Align javascripts with their <SCRIPT> tag.
mikeabdullah authored
366 [self decreaseIndentationLevel];
a84c9d5 Mike Abdullah Nicely apply indentation to scripts.
mikeabdullah authored
367 [self startNewline];
12b9584 Mike Abdullah sourceless <SCRIPT>s likely don't want to be inline.
mikeabdullah authored
368 [self stopWritingInline];
369 }
2d68556 Mike Abdullah Embedded scripts should also write their end tag on a new line.
mikeabdullah authored
370 }
1ca811e Mike Abdullah Split out -startJavascriptElementWithSrc:
mikeabdullah authored
371 }
372
f241869 Mike Abdullah 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 wo...
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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
397 #pragma mark Style
398
6dd07ee Mike Abdullah 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 Mike Abdullah 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 wo...
Mike authored
408 if (type) [self pushAttribute:@"type" value:type];
9beb73b Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
409 [self startElement:@"style"];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
410 }
411
412 #pragma mark Elements Stack
413
1b3d197 Mike Abdullah KSHTMLWriter gains:
mikeabdullah authored
414 - (BOOL)hasListOpen;
415 {
416 return ([self hasOpenElement:@"ul"] || [self hasOpenElement:@"ol"]);
417 }
418
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
419 - (BOOL)topElementIsList;
420 {
1b3d197 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
428 return result;
429 }
430
c35c721 Mike Abdullah 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 Mike Abdullah 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 Mike Abdullah Only a handful of HTML elements are allowed to be empty.
mikeabdullah authored
452
8c20ada Mike Abdullah Improve performance.
mikeabdullah authored
453 return [emptyTags containsObject:tagName];
c35c721 Mike Abdullah Only a handful of HTML elements are allowed to be empty.
mikeabdullah authored
454 }
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
455
89aba47 Mike Abdullah Refactor inline writing decisions across an instance & class method.
mikeabdullah authored
456 + (BOOL)shouldPrettyPrintElementInline:(NSString *)elementName;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
457 {
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
458 switch ([elementName length])
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
459 {
460 case 1:
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
461 if ([elementName isEqualToString:@"a"] ||
462 [elementName isEqualToString:@"b"] ||
463 [elementName isEqualToString:@"i"] ||
d70ebbd Mike Abdullah <STRIKE> and <S> can be written inline.
mikeabdullah authored
464 [elementName isEqualToString:@"s"] ||
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
465 [elementName isEqualToString:@"u"] ||
466 [elementName isEqualToString:@"q"]) return YES;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
467 break;
468
469 case 2:
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
470 if ([elementName isEqualToString:@"br"] ||
471 [elementName isEqualToString:@"em"] ||
472 [elementName isEqualToString:@"tt"]) return YES;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
473 break;
474
475 case 3:
d1e15e8 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
487 break;
488
489 case 4:
d1e15e8 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
496 break;
497
498 case 5:
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
499 if ([elementName isEqualToString:@"small"] ||
500 [elementName isEqualToString:@"input"] ||
501 [elementName isEqualToString:@"label"]) return YES;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
502 break;
503
504 case 6:
d1e15e8 Mike Abdullah 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 Mike Abdullah <STRIKE> and <S> can be written inline.
mikeabdullah authored
510 [elementName isEqualToString:@"script"] ||
511 [elementName isEqualToString:@"strike"]) return YES;
782c637 Mike Abdullah Expand the list of known inline elements
mikeabdullah authored
512 break;
513
514 case 7:
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
515 if ([elementName isEqualToString:@"acronym"]) return YES;
782c637 Mike Abdullah Expand the list of known inline elements
mikeabdullah authored
516 break;
517
518 case 8:
d1e15e8 Mike Abdullah correct terminology
mikeabdullah authored
519 if ([elementName isEqualToString:@"textarea"]) return YES;
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
520 break;
521 }
522
89aba47 Mike Abdullah Refactor inline writing decisions across an instance & class method.
mikeabdullah authored
523 return [super shouldPrettyPrintElementInline:elementName];
994c91e Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
524 }
525
05b69fd Mike Abdullah 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 Mike Abdullah 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 Mike Abdullah And the real star of our show arrives: KSHTMLWriter!
mikeabdullah authored
555 #pragma mark Element Primitives
556
c8bc317 Mike Abdullah Can now ditch -openTag:writeInline:
mikeabdullah authored
557 - (void)startElement:(NSString *)elementName writeInline:(BOOL)writeInline; // for more control
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
558 {
1fa7ee9 Mike Abdullah Checking HTML tags is a waste of time outside of debug builds I'd judge.
mikeabdullah authored
559 #ifdef DEBUG
566d24c Mike Abdullah 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 Mike Abdullah Checking HTML tags is a waste of time outside of debug builds I'd judge.
mikeabdullah authored
561 #endif
566d24c Mike Abdullah non-lowercase tags shouldn't be used for HTML.
mikeabdullah authored
562
563
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
564 // Add in any pre-written classes
5df9cd7 Mike Abdullah Don't bother exposing -[KSHTMLWriter elementClassName]. Can use [[[write...
mikeabdullah authored
565 NSString *class = [self currentElementClassName];
ecfabe0 Mike Abdullah Expose -className.
mikeabdullah authored
566 if (class)
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
567 {
568 [_classNames removeAllObjects];
b12f4c9 Sorry to keep changing this, but -pushElementAttribute: really is too wo...
Mike authored
569 [super pushAttribute:@"class" value:class];
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
570 }
9beb73b Mike Abdullah Take a bunch of primitive element APIs private. Just need to use -addAtt...
mikeabdullah authored
571
c8bc317 Mike Abdullah Can now ditch -openTag:writeInline:
mikeabdullah authored
572 [super startElement:elementName writeInline:writeInline];
75c4ca6 Mike Abdullah Experimental support for building up CSS class names before writing an e...
mikeabdullah authored
573 }
574
994c91e Mike Abdullah 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.