Showing preview only (349K chars total). Download the full file or copy to clipboard to get everything.
Repository: nicolasgoutaland/GONMarkupParser
Branch: master
Commit: fc0277420964
Files: 107
Total size: 321.3 KB
Directory structure:
gitextract_dlvgp0vj/
├── .gitignore
├── CHANGELOG.md
├── Classes/
│ ├── GONMarkup+Private.h
│ ├── GONMarkup+Private.m
│ ├── GONMarkup.h
│ ├── GONMarkup.m
│ ├── GONMarkupAlignment.h
│ ├── GONMarkupAlignment.m
│ ├── GONMarkupAnchor.h
│ ├── GONMarkupAnchor.m
│ ├── GONMarkupBlock.h
│ ├── GONMarkupBlock.m
│ ├── GONMarkupBold.h
│ ├── GONMarkupBold.m
│ ├── GONMarkupColor.h
│ ├── GONMarkupColor.m
│ ├── GONMarkupDec.h
│ ├── GONMarkupDec.m
│ ├── GONMarkupDefaultMarkups.h
│ ├── GONMarkupFont.h
│ ├── GONMarkupFont.m
│ ├── GONMarkupFontTraits.h
│ ├── GONMarkupFontTraits.m
│ ├── GONMarkupImage.h
│ ├── GONMarkupImage.m
│ ├── GONMarkupInc.h
│ ├── GONMarkupInc.m
│ ├── GONMarkupItalic.h
│ ├── GONMarkupItalic.m
│ ├── GONMarkupLineBreak.h
│ ├── GONMarkupLineBreak.m
│ ├── GONMarkupLineStyle.h
│ ├── GONMarkupLineStyle.m
│ ├── GONMarkupList.h
│ ├── GONMarkupList.m
│ ├── GONMarkupListItem.h
│ ├── GONMarkupListItem.m
│ ├── GONMarkupNamedColor.h
│ ├── GONMarkupNamedColor.m
│ ├── GONMarkupNamedFont.h
│ ├── GONMarkupNamedFont.m
│ ├── GONMarkupParagraph.h
│ ├── GONMarkupParagraph.m
│ ├── GONMarkupParser.h
│ ├── GONMarkupParser.m
│ ├── GONMarkupParserManager.h
│ ├── GONMarkupParserManager.m
│ ├── GONMarkupParserUtils.h
│ ├── GONMarkupParserUtils.m
│ ├── GONMarkupParser_All.h
│ ├── GONMarkupParser_Categories.h
│ ├── GONMarkupReset.h
│ ├── GONMarkupReset.m
│ ├── GONMarkupSimple.h
│ ├── GONMarkupSimple.m
│ ├── GONMarkupStrong.h
│ ├── GONMarkupStrong.m
│ ├── GONMarkupTextStyle.h
│ ├── GONMarkupTextStyle.m
│ ├── UIButton+GONMarkupParser.h
│ ├── UIButton+GONMarkupParser.m
│ ├── UILabel+GONMarkupParser.h
│ ├── UILabel+GONMarkupParser.m
│ ├── UITextField+GONMarkupParser.h
│ ├── UITextField+GONMarkupParser.m
│ ├── UITextView+GONMarkupParser.h
│ └── UITextView+GONMarkupParser.m
├── Example/
│ ├── GONMarkupParserSample/
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── ComplexExample
│ │ ├── ComplexExampleViewController.h
│ │ ├── ComplexExampleViewController.m
│ │ ├── ComplexExampleViewController.xib
│ │ ├── DefaultConfigurationViewController.h
│ │ ├── DefaultConfigurationViewController.m
│ │ ├── DefaultConfigurationViewController.xib
│ │ ├── GONMarkupParserSample-Info.plist
│ │ ├── GONMarkupParserSample-Prefix.pch
│ │ ├── Images.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.launchimage/
│ │ │ └── Contents.json
│ │ ├── MainWindow.xib
│ │ ├── PlaygroundViewController.h
│ │ ├── PlaygroundViewController.m
│ │ ├── PlaygroundViewController.xib
│ │ ├── ResultViewController.h
│ │ ├── ResultViewController.m
│ │ ├── ResultViewController.xib
│ │ ├── RootViewController.h
│ │ ├── RootViewController.m
│ │ ├── RootViewController.xib
│ │ ├── SampleViewController.h
│ │ ├── SampleViewController.m
│ │ ├── SampleViewController.xib
│ │ ├── en.lproj/
│ │ │ └── InfoPlist.strings
│ │ ├── main.m
│ │ └── samples.plist
│ ├── GONMarkupParserSample.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ ├── GONMarkupParserSample.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ ├── GONMarkupParserSampleTests/
│ │ ├── GONMarkupParserSampleTests-Info.plist
│ │ ├── GONMarkupParserSampleTests.m
│ │ └── en.lproj/
│ │ └── InfoPlist.strings
│ └── PodFile
├── GONMarkupParser.podspec
├── LICENSE
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
.DS_Store
*/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
*.hmap
*.xccheckout
*.xcuserstate
#CocoaPods
Pods
/Example/GONMarkupParserSample.xcodeproj/project.xcworkspace/xcshareddata
================================================
FILE: CHANGELOG.md
================================================
# Change Log
## [__0.8.2__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.8.2)
Updated HTML entities replace code<br/>
## [__0.8.1__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.8.1)
Fixed list alignment bug in iOS12<br/>
## [__0.8.0__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.8.0)
Improved Paragraph layout<br/>
Improved List Item layout<br/>
Added paragraph sample<br/>
Fixed a typo issue with paragraph class<br/>
## [__0.7.9__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.9)
Fixed bug in GONMarkupParserUtils<br/>
## [__0.7.8__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.8)
GONMarkupParserUtils optimisation<br/>
## [__0.7.7__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.7)
Fixed sample Podfile<br/>
## [__0.7.6__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.6)
Fixed a bug on NSParagraphStyle mutability<br/>
## [__0.7.5__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.5)
Fixed a bug on lists heading alignment<br/>
Fixed a bug on lists, with subitems not aligned<br/>
Added support for strong tag (yoohoo \o/)<br/>
## [__0.7.4__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.4)
Fixed (again -_-) new line character bug on lists<br/>
## [__0.7.3__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.3)
Fixed new line character bug on lists<br/>
## [__0.7.2__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.2)
Fixed bug #21
## [__0.7.1__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.1)
Fixed warning #18
## [__0.7.0__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.7.0)
Fixed bug #15
## [__0.6.9__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.9)
Fixed bug #12
## [__0.6.8__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.8)
Fixed import problems with cocoa Pods 0.39
## [__0.6.7__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.7)
Added color attribute to font markup
## [__0.6.6__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.6)
Added UIButton+GONMarkupParser category
## [__0.6.5__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.5)
Updated HTML chars tables
## [__0.6.4__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.4)
Updated HTML chars tables
## [__0.6.3__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.3)
Fixed a bug in anchor support
## [__0.6.2__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.2)
Added anchor support
## [__0.6.1__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6.1)
Added support for underline and strikethrough color
## [__0.6__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.6)
Added support for underline / strikethrough text
## [__0.5__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.5)
Initial release
================================================
FILE: Classes/GONMarkup+Private.h
================================================
//
// GONMarkup+Private.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkup.h"
#import "GONMarkupParser.h"
@interface GONMarkup (Private)
// Shared context configuration LIFO management
// These methods can be used to shared data between markers throught shared context.
/* Push a new configuration to shared context, under given key */
- (void)pushConfiguration:(id)configuration toContext:(NSMutableDictionary *)context forKey:(NSString *)key;
/* Return current available configuration in shared context for given key
* May return nil if there is no configuration
*/
- (id)currentContextConfiguration:(NSString *)key fromContext:(NSMutableDictionary *)context;
/* Pop current configuration from shared context, and return it.
* May return nil if there is no configuration */
- (id)popContextConfiguration:(NSString *)key fromContext:(NSMutableDictionary *)context;
@property (nonatomic, weak) GONMarkupParser *parser;
@end
================================================
FILE: Classes/GONMarkup+Private.m
================================================
//
// GONMarkup+Private.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkup+Private.h"
@implementation GONMarkup (Private)
#pragma mark - LIFO shared context management
- (void)pushConfiguration:(id)configuration
toContext:(NSMutableDictionary *)context
forKey:(NSString *)key
{
// Retrieve FIFO
NSMutableArray *configurationsLIFO = [context objectForKey:key];
if (!configurationsLIFO)
{
// Create FIFO
configurationsLIFO = [[NSMutableArray alloc] init];
[context setObject:configurationsLIFO
forKey:key];
}
// Hold current configuration
[configurationsLIFO addObject:configuration];
}
- (id)currentContextConfiguration:(NSString *)key
fromContext:(NSMutableDictionary *)context
{
return [[context objectForKey:key] lastObject];
}
- (id)popContextConfiguration:(NSString *)key
fromContext:(NSMutableDictionary *)context
{
// Retrieve LIFO
NSMutableArray *configurationsLIFO = [context objectForKey:key];
// Retrieve current configuration
id currentConfiguration = [configurationsLIFO lastObject];
// Remove last configuration
[configurationsLIFO removeLastObject];
// Check if list is empty
if (!configurationsLIFO.count)
{
// Remove empty list
[context removeObjectForKey:key];
}
return currentConfiguration;
}
@dynamic parser;
@end
================================================
FILE: Classes/GONMarkup.h
================================================
//
// GONMarkup.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
// Simple class defining a markup
// You can use it as it, or override it to add more configuration
//
// Tag name will be stored lowercased, so be careful when using multiple tags
//
// Markup lifecycle :
// - Once a tag is found, openingMarkupFound:configuration:context: method will be called
// - Before parser append extracted string to result, following methods will be called
// - prefixStringForContext:attributes:resultString: will be called, allowing tag to generate a prefix
// - updatedContentString:context:attributes:resultString: will be called, allowing tag to update its content string (without prefix included)
// - suffixStringForContext:attributes:resultString: will be called, allowing tag to generate a suffix
// - Once tag is closed, closingMarkupFound:configuration:context:resultString: method will be called
//
// Attributes should be space separated, values affected using equal sign, and between quotes or double quotes. To escape quotes / double quotes, use \
// Be careful,text heere is intended to be loaded from a file, not directly set in code. If so, do not forget to escape \.
// You can also use "
//
// Example :
// Attribute | Supported
// -----------------------------------
// <color value="red"> | YES
// <color value = "red"> | YES
// <color value = red> | NO
// <color value = "re"d"> | NO
// <color value = "re\"d"> | YES
// <color value='red'> | YES
// <color value = 'red'> | YES
// <color value = red> | NO
// <color value = "re'd"> | YES
// <color value = 're"d'> | YES
//
//
// Markup instance will be reused each time a matching tag is found. To persist data, use context parameter.
@class GONMarkupParser;
@interface GONMarkup : NSObject
/* Class constructor */
+ (instancetype)markupForTag:(NSString *)tag;
/* Default constructor */
- (id)initWithTag:(NSString *)tag;
/* This method will be called if markup is matching current opening tag.
* Object is responsible to update attributed string parameters in "configurationDictionary"
*
* "tag" is matching tag, allowing you to extract parameters
* "context" is a mutable dictionary used by marker to add contextual information. This dictionary is shared throught all markers in a parser and is reset each time a new parse is started
* It is used for example by list markers to handle list type, tabulation index and count
* "resultString" is currently built result string
*
* You should override this method to implement new behavior
*/
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString;
/* Allows marker to prefix its content string
* This method is called right after opening markup
*/
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttribute
resultString:(NSAttributedString *)resultString;
/* This method will be called once current marker tag is closed
* This allows marker to update string content
* "context" is the same as in openingMarkupFound:configuration:fromTag:context:
*
* Default implementation return input string
*/
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString;
/* Allows marker to suffix its content string
* This method is called right after opening markup
*/
- (NSAttributedString *)suffixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString;
/* This method will be called if markup is matching current closing tag.
* Object is responsible to update attributed string parameters in "configurationDictionary"
*
* "tag" is matching tag, allowing you to extract parameters
* "context" is a mutable dictionary use by marker to add contextual information. This dictionary is shared throught all markers in a parser and is reset each time a new parse is started
* It is used for example by list markers to handle list type, tabulation index and count
*
* You should override this method to implement new behavior
*/
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString;
@property (nonatomic, copy, readonly) NSString *tag; // Have to be unique. Used to speed up rules matching, when using tags without parameters
@property (nonatomic, weak, readonly) GONMarkupParser *parser; // Parser the markup is attached to
@end
================================================
FILE: Classes/GONMarkup.m
================================================
//
// GONMarkup.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
#import "GONMarkup.h"
#import "GONMarkupParser.h"
@interface GONMarkup()
// Data
@property (nonatomic, copy) NSString *tag;
@property (nonatomic, copy) NSString *testedTag;
// Parser link
@property (nonatomic, weak) GONMarkupParser *parser;
@end
@implementation GONMarkup
#pragma mark - Constructors
+ (instancetype)markupForTag:(NSString *)tag
{
return [[self alloc] initWithTag:tag];
}
- (id)initWithTag:(NSString *)tag
{
if (self = [super init])
{
_tag = [[tag lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
_testedTag = [_tag stringByAppendingString:@" "];
}
return self;
}
- (id)init
{
@throw @"Error, - init constructor is not allowed. Markup MUST have a tag";
}
#pragma mark - Test
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
return [[NSAttributedString alloc] initWithString:string attributes:stringAttributes];
}
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
{
NSLog(@"WARNING : This method will be deleted in next release. Use updatedContentString:context:attributes:stringAttributes:resultString: instead");
return [self updatedContentString:string
context:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:nil];
}
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
return [[NSAttributedString alloc] initWithString:@""];
}
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
{
NSLog(@"WARNING : This method will be deleted in next release. Use prefixStringForContext:attributes:stringAttributes:resultString: instead");
return [self prefixStringForContext:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:nil];
}
- (NSAttributedString *)suffixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
{
NSLog(@"WARNING : This method will be deleted in next release. Use suffixStringForContext:attributes:stringAttributes:resultString: instead");
return [self suffixStringForContext:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:nil];
}
- (NSAttributedString *)suffixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
return [[NSAttributedString alloc] initWithString:@""];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString {}
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes {
NSLog(@"WARNING : This method will be deleted in next release. Use openingMarkupFound:configuration:context:attributes:resultString: instead");
[self openingMarkupFound:tag
configuration:configurationDictionary
context:context
attributes:dicAttributes
resultString:nil];
}
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes {
NSLog(@"WARNING : This method will be deleted in next release. Use closingMarkupFound:configuration:context:attributes:resultString: instead");
[self closingMarkupFound:tag
configuration:configurationDictionary
context:context
attributes:dicAttributes
resultString:nil];
}
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString {}
@end
================================================
FILE: Classes/GONMarkupAlignment.h
================================================
//
// GONMarkupAlignment.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 22/07/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
// Markup used to handle text alignment.
// This markup will set a NSMutableParagraphStyle object under NSParagraphStyleAttributeName key.
// Be sure to make a mutable copy of it before updating when adding new tags
//
// Examples
//
// <left>left aligned text</>
// <right>right aligned text</>
// <center>center aligned text</>
// <justified>justified aligned text</>
// <natural>natural aligned text</>
#import "GONMarkup.h"
// Tag
#define GONMarkupAlignment_left_TAG @"left"
#define GONMarkupAlignment_right_TAG @"right"
#define GONMarkupAlignment_center_TAG @"center"
#define GONMarkupAlignment_justified_TAG @"justified"
#define GONMarkupAlignment_natural_TAG @"natural"
@interface GONMarkupAlignment : GONMarkup
/* Return all alignment markups */
+ (NSArray *)allMarkups;
/* Default markup to add center text alignment support */
+ (instancetype)centerMarkup;
/* Default markup to add left text alignment support */
+ (instancetype)leftMarkup;
/* Default markup to add right text alignment support */
+ (instancetype)rightMarkup;
/* Default markup to add justified text alignment support */
+ (instancetype)justifiedMarkup;
/* Default markup to add natural text alignment support */
+ (instancetype)naturalMarkup;
@end
================================================
FILE: Classes/GONMarkupAlignment.m
================================================
//
// GONMarkupAlignment.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 22/07/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupAlignment.h"
@interface GONMarkupAlignment ()
// Data
@property (nonatomic, assign) NSTextAlignment alignment;
@end
@implementation GONMarkupAlignment
#pragma mark - Markup list
+ (NSArray *)allMarkups
{
return @[[self centerMarkup], [self leftMarkup], [self rightMarkup], [self justifiedMarkup], [self naturalMarkup]];
}
#pragma mark - Constructors
+ (instancetype)alignmentMarkup:(NSTextAlignment)alignment tag:(NSString *)tag
{
GONMarkupAlignment *markupAlignment = [self markupForTag:tag];
markupAlignment.alignment = alignment;
return markupAlignment;
}
+ (instancetype)centerMarkup;
{
return [self alignmentMarkup:NSTextAlignmentCenter tag:GONMarkupAlignment_center_TAG];
}
+ (instancetype)leftMarkup;
{
return [self alignmentMarkup:NSTextAlignmentLeft tag:GONMarkupAlignment_left_TAG];
}
+ (instancetype)rightMarkup;
{
return [self alignmentMarkup:NSTextAlignmentRight tag:GONMarkupAlignment_right_TAG];
}
+ (instancetype)justifiedMarkup;
{
return [self alignmentMarkup:NSTextAlignmentJustified tag:GONMarkupAlignment_justified_TAG];
}
+ (instancetype)naturalMarkup;
{
return [self alignmentMarkup:NSTextAlignmentNatural tag:GONMarkupAlignment_natural_TAG];
}
#pragma mark - Markup lifecycle
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
NSMutableParagraphStyle *style = [[configurationDictionary objectForKey:NSParagraphStyleAttributeName] mutableCopy];
if (!style)
style = [[NSMutableParagraphStyle alloc] init];
// Hold new configuration
[configurationDictionary setObject:style
forKey:NSParagraphStyleAttributeName];
// Update alignment
style.alignment = _alignment;
}
@end
================================================
FILE: Classes/GONMarkupAnchor.h
================================================
//
// GONMarkupAnchor.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
// Define a markup to add anchor support
// You can specify link value with "value" attribute.
//
// To detect user touch on link :
// - display attributed string in a UITextView
// - configure UITextView selectable property to YES and isEditable to NO
// - set delegate
// - implement "textView:shouldInteractWithURL:inRange:" method
//
// Examples
//
// <a href="#1">Link 1</>
// <a href="http://www.apple.com">Link to apple.com</>
// <a href="myscheme://myapp">Custom link</>
#import "GONMarkup.h"
// Tag
#define GONMarkupAnchor_TAG @"a"
// Attributes
#define GONMarkupAnchor_TAG_href_ATT @"href"
@interface GONMarkupAnchor : GONMarkup
/* Default markup to add anchor support */
+ (instancetype)anchorMarkup;
@end
================================================
FILE: Classes/GONMarkupAnchor.m
================================================
//
// GONMarkupAnchor.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupAnchor.h"
@interface GONMarkupAnchor ()
@end
@implementation GONMarkupAnchor
#pragma mark - Constructor
+ (instancetype)anchorMarkup
{
return [self markupForTag:GONMarkupAnchor_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
NSString *value = [dicAttributes objectForKey:GONMarkupAnchor_TAG_href_ATT];
if (value)
{
// Create URL based on value
[configurationDictionary setObject:value
forKey:NSLinkAttributeName];
}
}
@end
================================================
FILE: Classes/GONMarkupBlock.h
================================================
//
// GONMarkupBlock.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Markup using a block as parameters. Useful to add new markup without creating new subclasses
#import "GONMarkup.h"
@interface GONMarkupBlock : GONMarkup
/* Class contructor
*/
+ (instancetype)blockMarkup:(NSString *)tag;
@property (nonatomic, copy) void(^openingMarkupBlock)(NSMutableDictionary *configurationDictionary, NSString *tag, NSMutableDictionary *context, NSDictionary *dicAttributes, NSAttributedString *resultString); // Called when opening tag is found
@property (nonatomic, copy) void(^closingMarkupBlock)(NSMutableDictionary *configurationDictionary, NSString *tag, NSMutableDictionary *context, NSDictionary *dicAttributes, NSAttributedString *resultString); // Called when closing tag is found
@property (nonatomic, copy) NSAttributedString *(^prefixStringForContextBlock)(NSMutableDictionary *context, NSDictionary *dicAttributes, NSDictionary *stringAttributes, NSAttributedString *resultString); // Called to generate prefix
@property (nonatomic, copy) NSAttributedString *(^suffixStringForContextBlock)(NSMutableDictionary *context, NSDictionary *dicAttributes, NSDictionary *stringAttributes, NSAttributedString *resultString); // Called to generate suffix
@property (nonatomic, copy) NSAttributedString *(^updatedContentStringBlock)(NSString *string, NSMutableDictionary *context, NSDictionary *dicAttributes, NSDictionary *stringAttributes, NSAttributedString *resultString); // Called when extracted string is complete
@end
================================================
FILE: Classes/GONMarkupBlock.m
================================================
//
// GONMarkupBlock.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupBlock.h"
@implementation GONMarkupBlock
#pragma mark - Constructor
+ (instancetype)blockMarkup:(NSString *)tag
{
return [self markupForTag:tag];
}
#pragma mark - Configuration
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
if (_openingMarkupBlock)
_openingMarkupBlock(configurationDictionary, tag, context, dicAttributes, resultString);
}
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
if (_closingMarkupBlock)
_closingMarkupBlock(configurationDictionary, tag, context, dicAttributes, resultString);
}
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
if (_updatedContentStringBlock)
return _updatedContentStringBlock(string, context, dicAttributes, stringAttributes, resultString);
return [super updatedContentString:string
context:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:resultString];
}
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
if (_prefixStringForContextBlock)
return _prefixStringForContextBlock(context, dicAttributes, stringAttributes, resultString);
return [super prefixStringForContext:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:resultString];
}
- (NSAttributedString *)suffixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
if (_suffixStringForContextBlock)
return _suffixStringForContextBlock(context, dicAttributes, stringAttributes, resultString);
return [super suffixStringForContext:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:resultString];
}
@end
================================================
FILE: Classes/GONMarkupBold.h
================================================
//
// GONMarkupBold.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 19/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Tag updating current font style to bold
// This tag may not work if no bold version of current font is available.
// overrideBlock (GONMarkupFontTraits) allows you to override font. For example, you can used it to return a medium font instead of bold one.
// If overrideBlock is set, it will be called first. Is nil is returned, system will try to automatically resolve font
//
// Examples
//
// <b>bold text</>
#import "GONMarkupFontTraits.h"
#define GONMarkupBold_TAG @"b"
@interface GONMarkupBold : GONMarkupFontTraits
/* Default markup to add bold support */
+ (instancetype)boldMarkup;
@end
================================================
FILE: Classes/GONMarkupBold.m
================================================
//
// GONMarkupBold.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 19/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupBold.h"
@implementation GONMarkupBold
#pragma mark - Constructor
+ (instancetype)boldMarkup
{
return [super fontTraitsMarkup:GONMarkupBold_TAG
traits:UIFontDescriptorTraitBold];
}
@end
================================================
FILE: Classes/GONMarkupColor.h
================================================
//
// GONMarkupColor.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
// Define a generic markup to add color
// You can specify color value with "value" attribute.
//
// Colors are handled via NSString+Color, using "representedColor" category method
// Have a look at https://github.com/nicolasgoutaland/NSString-Color for more information
//
// Examples
//
// <color value="red">text</>
// <color value="#FFEEAA">text</>
// <color value="myCustomRegisteredColor">text</>
#import "GONMarkup.h"
// Tag
#define GONMarkupColor_TAG @"color"
// Attributes
#define GONMarkupColor_TAG_value_ATT @"value"
@interface GONMarkupColor : GONMarkup
/* Default markup to add color support */
+ (instancetype)colorMarkup;
@end
================================================
FILE: Classes/GONMarkupColor.m
================================================
//
// GONMarkupColor.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
#import "GONMarkupColor.h"
#import "NSString+Color.h"
@implementation GONMarkupColor
#pragma mark - Constructor
+ (instancetype)colorMarkup
{
return [self markupForTag:GONMarkupColor_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
UIColor *colorValue = [[dicAttributes objectForKey:GONMarkupColor_TAG_value_ATT] representedColor];
if (colorValue)
{
[configurationDictionary setObject:colorValue
forKey:NSForegroundColorAttributeName];
}
}
@end
================================================
FILE: Classes/GONMarkupDec.h
================================================
//
// GONMarkupDec.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 10/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Define a markup to decrement font size. By default, font is decreased by 1 point.
// You can specify another decrement value with "value" attribute.
//
// Examples
//
// <dec>text</>
// <dec value="5">text</>
#import "GONMarkup.h"
// Tag
#define GONMarkupDec_TAG @"dec"
// Attributes
#define GONMarkupDec_TAG_value_ATT @"value"
@interface GONMarkupDec : GONMarkup
/* Default markup to add text size decrement support */
+ (instancetype)decMarkup;
@end
================================================
FILE: Classes/GONMarkupDec.m
================================================
//
// GONMarkupDec.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 10/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupDec.h"
#define DEFAULT_DEC_VALUE 1
@implementation GONMarkupDec
#pragma mark - Constructor
+ (instancetype)decMarkup
{
return [super markupForTag:GONMarkupDec_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
NSInteger incValue = [[dicAttributes objectForKey:GONMarkupDec_TAG_value_ATT] intValue];
if (!incValue)
incValue = DEFAULT_DEC_VALUE;
// Look for current font
UIFont *currentFont = [configurationDictionary objectForKey:NSFontAttributeName];
if (!currentFont)
{
// No found defined, use default one with default size
currentFont = [UIFont systemFontOfSize:[UIFont systemFontSize]];
}
// Update current font with new size
[configurationDictionary setObject:[currentFont fontWithSize:MAX(currentFont.pointSize - incValue, 1)]
forKey:NSFontAttributeName];
}
@end
================================================
FILE: Classes/GONMarkupDefaultMarkups.h
================================================
//
// GONMarkupDefaultMarkups.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupAlignment.h"
#import "GONMarkupAnchor.h"
#import "GONMarkupBold.h"
#import "GONMarkupBlock.h"
#import "GONMarkupColor.h"
#import "GONMarkupDec.h"
#import "GONMarkupFont.h"
// WIP
//#import "GONMarkupImage.h"
#import "GONMarkupInc.h"
#import "GONMarkupItalic.h"
#import "GONMarkupLineBreak.h"
#import "GONMarkupLineStyle.h"
#import "GONMarkupList.h"
#import "GONMarkupListItem.h"
#import "GONMarkupNamedColor.h"
#import "GONMarkupNamedFont.h"
#import "GONMarkupParagraph.h"
#import "GONMarkupReset.h"
#import "GONMarkupSimple.h"
#import "GONMarkupStrong.h"
#import "GONMarkupTextStyle.h"
================================================
FILE: Classes/GONMarkupFont.h
================================================
//
// GONMarkupFont.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
// Define a generic markup to configure font
// "color" is used to change text color
// Colors are handled via NSString+Color, using "representedColor" category method
// Have a look at https://github.com/nicolasgoutaland/NSString-Color for more information
// "size" is used to define font size
// If missing, current font size will be used. If not found is set, default system font size will be used
// "name" define a registered font name or full font name
// - Markup will first try to find a registeredFont with given name.
// - If no font found, markup will try to load a font using given name
// If "name" isn't set, current defined font will be used with new defined size. If no font is currently used, default system one will be used
//
// If no attribute is set, current defined font will be removed (NSFontAttributeName), and default system one will be used instead
//
// Examples
//
// <font size="18">This text will use current font, set to 18</>
// <font name="Helvetica">This text will use Helvetica as font, using current font size</>
// <font name="Helvetica" size="18">This text will use Helvetica, set to 18</>
// <font name="Helvetica" size="18" color="red">This text will use Helvetica, set to 18, in red</>
#import "GONMarkup.h"
// Tag
#define GONMarkupFont_TAG @"font"
// Attributes
#define GONMarkupFont_TAG_size_ATT @"size" // Font size. If missing, will use default font size, or one previously set on stack
#define GONMarkupFont_TAG_color_ATT @"color" // Text color
#define GONMarkupFont_TAG_name_ATT @"name" // Full font name, including style
@interface GONMarkupFont : GONMarkup
/* Default markup to add font support */
+ (instancetype)fontMarkup;
@end
================================================
FILE: Classes/GONMarkupFont.m
================================================
//
// GONMarkupFont.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
#import "GONMarkupFont.h"
#import "GONMarkup+Private.h"
#import "NSString+Color.h"
@interface GONMarkupFont ()
@end
@implementation GONMarkupFont
#pragma mark - Constructor
+ (instancetype)fontMarkup
{
return [self markupForTag:GONMarkupFont_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
NSString *value;
BOOL resetFontAttribute = YES;
// Text color
value = [dicAttributes objectForKey:GONMarkupFont_TAG_color_ATT];
if (value)
{
[configurationDictionary setObject:[value representedColor]
forKey:NSForegroundColorAttributeName];
resetFontAttribute = NO;
}
// Font name
value = [dicAttributes objectForKey:GONMarkupFont_TAG_name_ATT];
if (value)
{
// Look for size attribute
CGFloat size;
NSString *sizeValue = [dicAttributes objectForKey:GONMarkupFont_TAG_size_ATT];
if (sizeValue)
{
size = [sizeValue floatValue];
}
else
{
UIFont *currentFont = [configurationDictionary objectForKey:NSFontAttributeName];
if (currentFont)
size = currentFont.pointSize;
else
size = [UIFont systemFontSize];
}
// Try to load font from registered ones
UIFont *font = [self.parser fontForKey:value];
if (!font)
{
// No matching font found, try to load it by name
font = [UIFont fontWithName:value size:size];
}
else
{
// Font found, update its size
font = [UIFont fontWithDescriptor:font.fontDescriptor size:[sizeValue floatValue]];
}
// Update configuration
[configurationDictionary setObject:font
forKey:NSFontAttributeName];
resetFontAttribute = NO;
}
else
{
// Font size only
value = [dicAttributes objectForKey:GONMarkupFont_TAG_size_ATT];
if (value)
{
// Extract size
CGFloat size = [value floatValue];
// Look for current font
UIFont *currentFont = [configurationDictionary objectForKey:NSFontAttributeName];
if (currentFont)
{
// Current font found, so update it with new size
currentFont = [UIFont fontWithDescriptor:currentFont.fontDescriptor
size:size];
}
else
{
// No found defined, use default one with defined size
currentFont = [UIFont systemFontOfSize:size];
}
// Update configuration
[configurationDictionary setObject:currentFont
forKey:NSFontAttributeName];
resetFontAttribute = NO;
}
}
// Empty font parameter, reset configuration
if (resetFontAttribute)
[configurationDictionary removeObjectForKey:NSFontAttributeName];
}
@end
================================================
FILE: Classes/GONMarkupFontTraits.h
================================================
//
// GONMarkupFontTraits.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 22/09/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
// Super class for markers updating font traits
// overrideBlock allows you to override font. For example, you can used it to return a medium font instead of bold one.
// If overrideBlock is set, it will be called first. Is nil is returned, sysytem will try to automatically resolve font
// Also note that if an overrideBlock is set, it will be called even is current font have needed trait.
//
// You may not need to use this class directly. Use subclasses instead (GONMarkupBold, GONMarkupItalic)
#import "GONMarkup.h"
@interface GONMarkupFontTraits : GONMarkup {
}
/* Class constructor */
+ (instancetype)fontTraitsMarkup:(NSString *)tag traits:(UIFontDescriptorSymbolicTraits )trait;
@property (nonatomic, copy) UIFont *(^overrideBlock)(UIFont *font); // Should return a font. Markup will automatically update font to needed size.
@property (nonatomic, assign, readonly) UIFontDescriptorSymbolicTraits trait;
@end
================================================
FILE: Classes/GONMarkupFontTraits.m
================================================
//
// GONMarkupFontTraits.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 22/09/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupFontTraits.h"
#import "GONMarkup+Private.h"
@interface GONMarkupFontTraits ()
// Data
@property (nonatomic, assign) UIFontDescriptorSymbolicTraits trait;
@end
@implementation GONMarkupFontTraits
#pragma mark - Constructor
+ (instancetype)fontTraitsMarkup:(NSString *)tag traits:(UIFontDescriptorSymbolicTraits )trait
{
GONMarkupFontTraits *markup = [self markupForTag:tag];
markup.trait = trait;
return markup;
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Look for current font
UIFont *currentFont = [configurationDictionary objectForKey:NSFontAttributeName];
if (!currentFont)
{
// No found defined, use default one with default size
currentFont = [UIFont systemFontOfSize:[UIFont systemFontSize]];
}
UIFont *updatedFont = nil;
// Check override block
if (_overrideBlock)
{
// Try with override block
updatedFont = _overrideBlock(currentFont);
}
// Check if font already has traits, and if override blocks didn't return a font
if (!updatedFont)
{
// Update font to set trait
UIFontDescriptor *fontDescriptor = currentFont.fontDescriptor;
UIFontDescriptorSymbolicTraits fontTaits = fontDescriptor.symbolicTraits;
if (!(fontTaits & _trait))
{
fontTaits |= _trait;
updatedFont = [UIFont fontWithDescriptor:[fontDescriptor fontDescriptorWithSymbolicTraits:fontTaits]
size:currentFont.pointSize];
// Font may not exists, fallback
// Note : In iOS7, if no fount is found, normal one will be returned. Since iOS8, nil will be returned
if (!updatedFont || [currentFont isEqual:updatedFont])
{
if (self.parser.logLevel & GONMarkupParserLogLevelFonts)
{
if (!_overrideBlock)
NSLog(@"%@ : No font found for <%@-%@> applying traits. Consider setting up <overrideBlock> to provide a font", [[self class] description], currentFont.familyName, currentFont.fontName);
else
NSLog(@"%@ : No font returned from overrideBlock for <%@-%@>. Consider seting up one", [[self class] description], currentFont.familyName, currentFont.fontName);
}
// Do not update font
updatedFont = currentFont;
}
}
else
{
// Font has already requested trait, so use it
updatedFont = currentFont;
}
}
// Check for font size
if (updatedFont.pointSize != currentFont.pointSize)
{
// Build a new font with current size
updatedFont = [UIFont fontWithDescriptor:[updatedFont fontDescriptor]
size:currentFont.pointSize];
}
// Update configuration
[configurationDictionary setObject:updatedFont
forKey:NSFontAttributeName];
}
@end
================================================
FILE: Classes/GONMarkupImage.h
================================================
//
// GONMarkupImage.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/03/16.
// Copyright © 2016 Nicolas Goutaland. All rights reserved.
//
//
// Define a generic markup to add images
// You can specify image path with "value" attribute.
//
// Examples
//
// <image src="image.png"/>
// <image src="#FFEEAA"/>
// <image src="myCustomRegisteredColor"/>
#import "GONMarkup.h"
// Tag
#define GONMarkupImage_TAG @"image"
// Attributes
#define GONMarkupImage_TAG_value_ATT @"src"
@interface GONMarkupImage : GONMarkup
/* Default markup to add image support */
+ (instancetype)imageMarkup;
@end
================================================
FILE: Classes/GONMarkupImage.m
================================================
//
// GONMarkupImage.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/03/16.
// Copyright © 2016 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupImage.h"
@implementation GONMarkupImage
+ (instancetype)imageMarkup
{
return [self markupForTag:@"image"];
}
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
UIImage *image = [UIImage imageNamed:[dicAttributes objectForKey:GONMarkupImage_TAG_value_ATT]];
if (image)
{
CGFloat scale = [UIScreen mainScreen].scale;
scale = 2;
NSTextAttachment *attch = [[NSTextAttachment alloc] init];
attch.image = image;
attch.bounds = CGRectMake(0, 0, image.size.width/scale, image.size.height/scale);
return [NSAttributedString attributedStringWithAttachment:attch];
}
return [super updatedContentString:string
context:context
attributes:dicAttributes
stringAttributes:stringAttributes
resultString:resultString];
}
@end
================================================
FILE: Classes/GONMarkupInc.h
================================================
//
// GONMarkupInc.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 10/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Define a markup to increment font size. By default, font is increased by 1 point.
// You can specify another increment value with "value" attribute.
//
// Examples
//
// <inc>text</>
// <inc value="5">text</>
#import "GONMarkup.h"
// Tag
#define GONMarkupInc_TAG @"inc"
// Attributes
#define GONMarkupInc_TAG_value_ATT @"value"
@interface GONMarkupInc : GONMarkup
/* Default markup to add text size increment support */
+ (instancetype)incMarkup;
@end
================================================
FILE: Classes/GONMarkupInc.m
================================================
//
// GONMarkupInc.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 10/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupInc.h"
#define DEFAULT_INC_VALUE 1
@implementation GONMarkupInc
#pragma mark - Constructor
+ (instancetype)incMarkup
{
return [super markupForTag:GONMarkupInc_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
NSInteger incValue = [[dicAttributes objectForKey:GONMarkupInc_TAG_value_ATT] intValue];
if (!incValue)
incValue = DEFAULT_INC_VALUE;
// Look for current font
UIFont *currentFont = [configurationDictionary objectForKey:NSFontAttributeName];
if (!currentFont)
{
// No found defined, use default one with default size
currentFont = [UIFont systemFontOfSize:[UIFont systemFontSize]];
}
// Update current font with new size
[configurationDictionary setObject:[currentFont fontWithSize:currentFont.pointSize + incValue]
forKey:NSFontAttributeName];
}
@end
================================================
FILE: Classes/GONMarkupItalic.h
================================================
//
// GONMarkupItalic.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 19/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Tag updating current font style to italic
// This tag may not work if no italic version of current font is available.
// overrideBlock (GONMarkupFontTraits) allows you to override font. For example, you can used it to return a medium italic font instead of bold italic one.
// If overrideBlock is set, it will be called first. Is nil is returned, system will try to automatically resolve font
//
// Examples
//
// <i>italic text</>
#import "GONMarkupFontTraits.h"
#define GONMarkupItalic_TAG @"i"
@interface GONMarkupItalic : GONMarkupFontTraits
/* Default markup to add italic support */
+ (instancetype)italicMarkup;
@end
================================================
FILE: Classes/GONMarkupItalic.m
================================================
//
// GONMarkupItalic.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 19/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupItalic.h"
#import "GONMarkup+Private.h"
@implementation GONMarkupItalic
#pragma mark - Constructor
+ (instancetype)italicMarkup
{
return [super fontTraitsMarkup:GONMarkupItalic_TAG
traits:UIFontDescriptorTraitItalic];
}
@end
================================================
FILE: Classes/GONMarkupLineBreak.h
================================================
//
// GONMarkupLineBreak.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Markup to handle <br/> characters, generating a new line
//
// Example
//
// <br />
#import "GONMarkup.h"
// Tag
#define GONMarkupLineBreak_TAG @"br"
@interface GONMarkupLineBreak : GONMarkup
/* Class constructor */
+ (instancetype)lineBreakMarkup;
@end
================================================
FILE: Classes/GONMarkupLineBreak.m
================================================
//
// GONMarkupLineBreak.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupLineBreak.h"
#import "GONMarkupList.h"
#define NEW_LINE @"\n"
#define CARRIAGE_RETURN @"\u2028"
@implementation GONMarkupLineBreak
#pragma mark - Constructor
+ (instancetype)lineBreakMarkup
{
return [self markupForTag:GONMarkupLineBreak_TAG];
}
#pragma mark - Content update
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
// If in a list, no new lines
if ([context objectForKey:GONMarkupList_CONFIGURATIONS_KEY])
return [[NSAttributedString alloc] initWithString:CARRIAGE_RETURN attributes:stringAttributes];
return [[NSAttributedString alloc] initWithString:NEW_LINE attributes:stringAttributes];
}
@end
================================================
FILE: Classes/GONMarkupLineStyle.h
================================================
//
// GONMarkupLineStyle.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 03/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
//
// Tag used to set underline / strikethrough style.
// Attribute "word" can be set to apply style only on words. Default is "false".
// Attribute "style" can be used to configure line style (single | thick | double). Default is "single".
// Attribute "pattern" can be used to configure pattern style (solid | dot | dash | dashdot | dashdotdot). Default is "solid".
// Attribute "color" can be used to configure line color. Default is the same as text".
//
// Colors are handled via NSString+Color, using "representedColor" category method
// Have a look at https://github.com/nicolasgoutaland/NSString-Color for more information
//
// Examples
//
// <u>underline text</>
// <u word="true">underline text, only for words</>
// <u pattern="dashdot">underline text with dashdot pattern</>
// <u pattern="dashdot" word="true">underline text with dashdot pattern, only for words</>
// <strike>strikethrough text</>
// <strike pattern="dashdot">strikethrough text with dashdot pattern</>
// <strike pattern="dashdot" word="true">strikethrough text with dashdot pattern, only for words</>
//
#import "GONMarkup.h"
// Tag
#define GONMarkupLineStyle_Underline_TAG @"u"
#define GONMarkupLineStyle_Strikethrough_TAG @"strike"
// Attributes
#define GONMarkupLineStyle_TAG_word_ATT @"word"
#define GONMarkupLineStyle_TAG_style_ATT @"style"
#define GONMarkupLineStyle_TAG_pattern_ATT @"pattern"
#define GONMarkupLineStyle_TAG_color_ATT @"color"
@interface GONMarkupLineStyle : GONMarkup
/* Return all markups */
+ (NSArray *)allMarkups;
/* Default markup to add underline support */
+ (instancetype)underlineMarkup;
/* Default markup to add strikethrough support */
+ (instancetype)strikethroughMarkup;
@end
================================================
FILE: Classes/GONMarkupLineStyle.m
================================================
//
// GONMarkupLineStyle.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 03/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupLineStyle.h"
#import "NSString+Color.h"
@interface GONMarkupLineStyle ()
// Data
@property (nonatomic, strong) NSString *configurationKey;
@property (nonatomic, strong) NSString *colorConfigurationKey;
@end
@implementation GONMarkupLineStyle
#pragma mark - Class initialization
static NSDictionary *dicStyles;
static NSDictionary *dicPatterns;
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dicStyles = @{
@"single" : @(NSUnderlineStyleSingle),
@"double" : @(NSUnderlineStyleDouble),
@"thick" : @(NSUnderlineStyleThick),
};
dicPatterns = @{
@"solid" : @(NSUnderlinePatternSolid),
@"dot" : @(NSUnderlinePatternDot),
@"dash" : @(NSUnderlinePatternDash),
@"dashdot" : @(NSUnderlinePatternDashDot),
@"dashdotdot" : @(NSUnderlinePatternDashDotDot)
};
});
}
#pragma mark - Constructor
+ (NSArray *)allMarkups
{
return @[[self underlineMarkup], [self strikethroughMarkup]];
}
+ (instancetype)underlineMarkup
{
GONMarkupLineStyle *markup = [self markupForTag:GONMarkupLineStyle_Underline_TAG];
markup.configurationKey = NSUnderlineStyleAttributeName;
markup.colorConfigurationKey = NSUnderlineColorAttributeName;
return markup;
}
+ (instancetype)strikethroughMarkup
{
GONMarkupLineStyle *markup = [self markupForTag:GONMarkupLineStyle_Strikethrough_TAG];
markup.configurationKey = NSStrikethroughStyleAttributeName;
markup.colorConfigurationKey = NSStrikethroughColorAttributeName;
return markup;
}
#pragma mark - Markup lifecycle
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Hold new configuration
[configurationDictionary setObject:@([self extractStyleFromAttributes:dicAttributes])
forKey:_configurationKey];
// Color
UIColor *color = [[dicAttributes objectForKey:GONMarkupLineStyle_TAG_color_ATT] representedColor];
if (color)
{
[configurationDictionary setObject:color
forKey:_colorConfigurationKey];
}
}
- (NSInteger)extractStyleFromAttributes:(NSDictionary *)dicAttributes
{
NSNumber *tmp;
NSInteger style = 0;
// Word option
if ([[dicAttributes objectForKey:GONMarkupLineStyle_TAG_word_ATT] boolValue])
style |= NSUnderlineByWord;
// Style option
tmp = [dicStyles objectForKey:[[dicAttributes objectForKey:GONMarkupLineStyle_TAG_style_ATT] lowercaseString]];
if (tmp)
style |= [tmp integerValue];
else
style |= NSUnderlineStyleSingle;
// Pattern option
tmp = [dicPatterns objectForKey:[[dicAttributes objectForKey:GONMarkupLineStyle_TAG_pattern_ATT] lowercaseString]];
if (tmp)
style |= [tmp integerValue];
else
style |= NSUnderlinePatternSolid;
return style;
}
@end
================================================
FILE: Classes/GONMarkupList.h
================================================
//
// GONMarkupList.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Markup used to handle lists
// You HAVE to add GONMarkupListItem support to handle list properly
//
// Example
//
// <ul>
// <li>first item</>
// <li>second item</>
// <li>third item</>
// </>
#import "GONMarkup.h"
// Tag
#define GONMarkupList_unordered_TAG @"ul"
#define GONMarkupList_ordered_TAG @"ol"
// Context
#define GONMarkupList_CONFIGURATIONS_KEY @"GONMarkupList.configurations" // References an array, holding automatically pushed / popped tags configurations
#define GONMarkupList_INDENTATION_KEY @"GONMarkupList.indentation"
#define GONMarkupList_POSITION_KEY @"GONMarkupList.position"
#define GONMarkupList_ORDERED_KEY @"GONMarkupList.ordered"
@interface GONMarkupList : GONMarkup
/* Return all markups, including list item one */
+ (NSArray *)allMarkups;
/* Default markup to add lists support */
+ (instancetype)listMarkup;
/* Default markup to add ordered lists support */
+ (instancetype)orderedListMarkup;
@end
================================================
FILE: Classes/GONMarkupList.m
================================================
//
// GONMarkupList.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupList.h"
#import "GONMarkupListItem.h"
#import "GONMarkup+Private.h"
@interface GONMarkupList ()
// Data
@property (nonatomic, assign) BOOL isOrdered;
@end
@implementation GONMarkupList
#pragma mark - Markup list
+ (NSArray *)allMarkups
{
return @[[self listMarkup], [self orderedListMarkup], [GONMarkupListItem listItemMarkup]];
}
#pragma mark - Constructor
+ (instancetype)listMarkup
{
GONMarkupList *markupList = [self markupForTag:GONMarkupList_unordered_TAG];
markupList.isOrdered = NO;
return markupList;
}
+ (instancetype)orderedListMarkup
{
GONMarkupList *markupList = [self markupForTag:GONMarkupList_ordered_TAG];
markupList.isOrdered =YES;
return markupList;
}
#pragma mark - Markup lifecycle
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Compute indentation level
NSInteger indentationLevel = 0;
NSDictionary *currentConfiguration = [self currentContextConfiguration:GONMarkupList_CONFIGURATIONS_KEY
fromContext:context];
if (currentConfiguration)
indentationLevel = [[currentConfiguration objectForKey:GONMarkupList_INDENTATION_KEY] intValue] + 1;
NSDictionary *configuration = [@{
GONMarkupList_ORDERED_KEY : @(_isOrdered),
GONMarkupList_INDENTATION_KEY : @(indentationLevel),
} mutableCopy];
[self pushConfiguration:configuration
toContext:context
forKey:GONMarkupList_CONFIGURATIONS_KEY];
}
- (NSAttributedString *)updatedContentString:(NSString *)string
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
return [[NSAttributedString alloc] initWithString:@""];
}
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
[self popContextConfiguration:GONMarkupList_CONFIGURATIONS_KEY
fromContext:context];
}
@end
================================================
FILE: Classes/GONMarkupListItem.h
================================================
//
// GONMarkupListItem.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Class describing a list item
// You can subclass it and override methods to handle custom bullets behavior
#import "GONMarkup.h"
// Tag
#define GONMarkupListItem_TAG @"li"
@interface GONMarkupListItem : GONMarkup
/* Default markup to add list item support */
+ (instancetype)listItemMarkup;
/* Method used to compute list item prefix, in ORDERED lists
* You can override this method in subclasses to change behavior
*
* indentationLevel : Current item indentation level in list, when list are nested
* position : Current item position in parent list.
* listConfiguration : Parent list configuration
* context : Shared context
*
* This method is expected to return a string. You MUST NOT return nil
*/
- (NSString *)orderedListItemPrefixForIndentation:(NSInteger)indentationLevel position:(NSInteger)position listConfiguration:(NSDictionary *)listConfiguration context:(NSMutableDictionary *)context;
/* Method used to compute list item prefix, in NON ORDERED lists
* You can override this method in subclasses to change behavior
*
* indentationLevel : Current item indentation level in list, when list are nested
* position : Current item position in parent list.
* listConfiguration : Parent list configuration
* context : Shared context
*
* This method is expected to return a string. You MUST NOT return nil
*/
- (NSString *)unorderedListItemPrefixForIndentation:(NSInteger)indentationLevel position:(NSInteger)position listConfiguration:(NSDictionary *)listConfiguration context:(NSMutableDictionary *)context;
@end
================================================
FILE: Classes/GONMarkupListItem.m
================================================
//
// GONMarkupListItem.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupListItem.h"
#import "GONMarkupList.h"
#import "GONMarkup+Private.h"
@implementation GONMarkupListItem
#pragma mark - Constructor
+ (instancetype)listItemMarkup
{
return [self markupForTag:GONMarkupListItem_TAG];
}
#pragma mark - Internal configuration
+ (NSString *)bulletForIndentation:(NSInteger)anIndentation
{
static NSArray *bullets = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bullets = @[@"\u2022", @"\u25E6"]; // Next should be @"\u25A0", @"\u25A1", but they are displayed too big
});
return [bullets objectAtIndex:anIndentation % bullets.count];
}
- (NSString *)unorderedListItemPrefixForIndentation:(NSInteger)indentationLevel
position:(NSInteger)position
listConfiguration:(NSDictionary *)aListConfiguration
context:(NSMutableDictionary *)context
{
NSString *indentation = [self listItemIndentation:indentationLevel];
NSString *bullet = [[self class] bulletForIndentation:indentationLevel];
return [NSString stringWithFormat:@"%@%@ ", indentation, bullet];
}
- (NSString *)orderedListItemPrefixForIndentation:(NSInteger)indentationLevel
position:(NSInteger)position
listConfiguration:(NSDictionary *)aListConfiguration
context:(NSMutableDictionary *)context
{
NSString *indentation = [self listItemIndentation:indentationLevel];
NSString *bullet = [NSString stringWithFormat:@"%ld.", (long)position];
return [NSString stringWithFormat:@"%@%@ ", indentation, bullet];
}
#pragma mark - Content update
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Retrieve indentation level
NSMutableDictionary *currentConfiguration = [self currentContextConfiguration:GONMarkupList_CONFIGURATIONS_KEY
fromContext:context];
[currentConfiguration setObject:@([[currentConfiguration objectForKey:GONMarkupList_POSITION_KEY] intValue] + 1)
forKey:GONMarkupList_POSITION_KEY];
// Retrieve and update paragraph style
NSMutableParagraphStyle *paragraphStyle = [self paragraphStyle:configurationDictionary];
// Generate prefix string
NSAttributedString *prefixString = [self prefixStringForContext:context
attributes:dicAttributes
stringAttributes:configurationDictionary
resultString:resultString];
// Compute prefix string width
paragraphStyle.headIndent = CGRectGetWidth([prefixString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil]);
}
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
// Retrieve configuration
NSDictionary *listConfiguration = [[context objectForKey:GONMarkupList_CONFIGURATIONS_KEY] lastObject];
NSInteger indentation = [[listConfiguration objectForKey:GONMarkupList_INDENTATION_KEY] intValue];
NSInteger position = [[listConfiguration objectForKey:GONMarkupList_POSITION_KEY] intValue];
BOOL isOrdered = [[listConfiguration objectForKey:GONMarkupList_ORDERED_KEY] intValue];
NSString *prefix;
if (isOrdered)
{
prefix = [self orderedListItemPrefixForIndentation:indentation
position:position
listConfiguration:listConfiguration
context:context];
}
else
{ prefix = [self unorderedListItemPrefixForIndentation:indentation
position:position
listConfiguration:listConfiguration
context:context];
}
// If starting a new list, we must have a blank line before previous text
if (position == 1 && indentation == 0)
{
// If last char isn't a new line, add a new line
if (resultString.string.length > 0 && ![[NSCharacterSet newlineCharacterSet] characterIsMember:[resultString.string characterAtIndex:resultString.string.length - 1]])
{
prefix = [@"\n\n" stringByAppendingString:prefix];
} // Last char was a newline, so check if a blank line already exists before paragraph
else if (resultString.string.length > 1 && ![[NSCharacterSet newlineCharacterSet] characterIsMember:[resultString.string characterAtIndex:resultString.string.length - 2]])
{
prefix = [@"\n" stringByAppendingString:prefix];
}
}
else
{
prefix = [@"\n" stringByAppendingString:prefix];
}
return [[NSAttributedString alloc] initWithString:prefix attributes:stringAttributes];
}
#pragma mark - Utils
- (NSMutableParagraphStyle *)paragraphStyle:(NSMutableDictionary *)configurationDictionary
{
NSMutableParagraphStyle *paragraphStyle = [[configurationDictionary objectForKey:NSParagraphStyleAttributeName] mutableCopy];
if (!paragraphStyle)
paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
// Hold updated paragraph style
[configurationDictionary setObject:paragraphStyle
forKey:NSParagraphStyleAttributeName];
return paragraphStyle;
}
- (NSString *)listItemIndentation:(NSInteger)anIndentation
{
NSMutableString *indentString = [[NSMutableString alloc] init];
for (NSInteger i=0; i<anIndentation; i++)
[indentString appendString:@"\t"];
return indentString;
}
@end
================================================
FILE: Classes/GONMarkupNamedColor.h
================================================
//
// GONMarkupNamedColor.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Define a tag to set foreground text color
// When encountered, contained text will use associated color
//
#import "GONMarkup.h"
@interface GONMarkupNamedColor : GONMarkup
/* Class constructor
* If aColor is nil, so this tag will reset current color to parser default one
*/
+ (instancetype)namedColorMarkup:(UIColor *)aColor forTag:(NSString *)tag;
@property (nonatomic, strong, readonly) UIColor *color;
@end
================================================
FILE: Classes/GONMarkupNamedColor.m
================================================
//
// GONMarkupNamedColor.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupNamedColor.h"
#import "GONMarkup+Private.h"
@interface GONMarkupNamedColor ()
// Data
@property (nonatomic, strong) UIColor *color;
@end
@implementation GONMarkupNamedColor
#pragma mark - Class constructor
+ (instancetype)namedColorMarkup:(UIColor *)aColor forTag:(NSString *)tag
{
GONMarkupNamedColor *markup = [self markupForTag:tag];
markup.color = aColor;
return markup;
}
#pragma mark - Configuration
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
if (_color)
{
[configurationDictionary setObject:_color
forKey:NSForegroundColorAttributeName];
}
else
{
UIColor *defaultColor = [[self.parser defaultConfiguration] objectForKey:NSForegroundColorAttributeName];
if (defaultColor)
{
[configurationDictionary setObject:defaultColor
forKey:NSForegroundColorAttributeName];
}
else
{
[configurationDictionary removeObjectForKey:NSForegroundColorAttributeName];
}
}
}
@end
================================================
FILE: Classes/GONMarkupNamedFont.h
================================================
//
// GONMarkupNamedFont.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Define a tag to set font / remove font
// When encountered, contained text will use associated font and color if set
#import "GONMarkup.h"
@interface GONMarkupNamedFont : GONMarkup
/* Class constructor
* If font is nil, so this tag will reset current font to parser default one
*/
+ (instancetype)namedFontMarkup:(UIFont *)font forTag:(NSString *)tag;
@property (nonatomic, strong, readonly) UIFont *font;
@end
================================================
FILE: Classes/GONMarkupNamedFont.m
================================================
//
// GONMarkupNamedFont.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupNamedFont.h"
#import "GONMarkup+Private.h"
@interface GONMarkupNamedFont ()
// Data
@property (nonatomic, strong) UIFont *font;
@end
@implementation GONMarkupNamedFont
#pragma mark - Class constructor
+ (instancetype)namedFontMarkup:(UIFont *)font forTag:(NSString *)tag
{
GONMarkupNamedFont *markup = [self markupForTag:tag];
markup.font = font;
return markup;
}
#pragma mark - Configuration
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
if (_font)
{
[configurationDictionary setObject:_font
forKey:NSFontAttributeName];
}
else
{
UIFont *defaultfont = [[self.parser defaultConfiguration] objectForKey:NSFontAttributeName];
if (defaultfont)
{
[configurationDictionary setObject:defaultfont
forKey:NSFontAttributeName];
}
else
{
[configurationDictionary removeObjectForKey:NSFontAttributeName];
}
}
}
@end
================================================
FILE: Classes/GONMarkupParagraph.h
================================================
//
// GONMarkupParagraph.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 06/08/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Markup to handle paragraphs
// A paragraph will automatically insert a new line after text
//
// Example
//
// <p>First paragraph</p>
//
#import "GONMarkup.h"
// Tag
#define GONMarkupParagraph_TAG @"p"
@interface GONMarkupParagraph : GONMarkup
/* Class constructor */
+ (instancetype)paragraphMarkup;
@end
================================================
FILE: Classes/GONMarkupParagraph.m
================================================
//
// GONMarkupParagraph.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 06/08/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Markup to handle <p>. A blank line will be inserted after the paragraph end (\n\n)
#import "GONMarkupParagraph.h"
@implementation GONMarkupParagraph
#pragma mark - Constructor
+ (instancetype)paragraphMarkup
{
return [self markupForTag:GONMarkupParagraph_TAG];
}
#pragma mark - Content update
- (NSAttributedString *)prefixStringForContext:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
stringAttributes:(NSDictionary *)stringAttributes
resultString:(NSAttributedString *)resultString
{
NSString *prefix = @"";
// Check for previous newline
if (resultString.string.length > 0)
{
// If last char isn't a new line, add a new line
if (![[NSCharacterSet newlineCharacterSet] characterIsMember:[resultString.string characterAtIndex:resultString.string.length - 1]])
{
prefix = @"\n\n";
} // Last char was a newline, so check if a blank line already exists before paragraph
else if (resultString.string.length > 1 && ![[NSCharacterSet newlineCharacterSet] characterIsMember:[resultString.string characterAtIndex:resultString.string.length - 2]])
{
prefix = @"\n";
}
}
return [[NSAttributedString alloc] initWithString:prefix attributes:stringAttributes];
}
@end
================================================
FILE: Classes/GONMarkupParser.h
================================================
//
// GONMarkupParser.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
// A simple string parser, allowing string manipulation, usually used to parse HTML like string.
// You have to define your own rules and ensure that tags are wellformed
//
// To close a tag, you do not have to specity its name, </> is valid
// <//> is also valid to close all previoulsy opened tags
//
// History
// -------
// 0.7.5 : Fixed bugs on lists, Added support for strong tag
// 0.7.4 : Fixed (again -_-) new line character bug on lists
// 0.7.3 : Fixed new line character bug on lists
// 0.7.2 : Fixed bug #21
// 0.7.1 : Fixed warning #18
// 0.7.0 : Fixed bug #15
// 0.6.9 : Fixed bug #12
// 0.6.8 : Fixed import problems with cocoa Pods 0.39
// 0.6.7 : Added color attribute to font markup
// 0.6.6 : Added UIButton+GONMarkupParser category
// 0.6.5 : Updated HTML chars tables
// 0.6.4 : Updated HTML chars tables
// 0.6.3 : Fixed a bug in anchor support
// 0.6.2 : Added anchor support
// 0.6.1 : Added support for underline and strikethrough color
// 0.6 : Added support for underline / strikethrough text
// 0.5 : Initial release
// Roadmap
// -------
// Evolution : Allow markup to be encoded (NSCoding)
// Evolution : Loading from PLIST file
// Evolution : Allow copy for parsers / markup
#import "GONMarkup.h"
#define GONMarkupParser_ERROR_DOMAIN @"error.markupParser"
#define GONMarkupParser_StringMalformed_ERROR_CODE 69
#define GONMarkupParser_incorrectClosingTag_KEY @"incorrectClosingTag"
typedef enum : NSUInteger {
GONMarkupParserLogLevelNone = 0, // No logs
GONMarkupParserLogLevelUnbalancedTags = 0x01 << 0, // Logs when unbalanced tags are found
GONMarkupParserLogLevelUnknownTag = 0x01 << 1, // Logs when an unknow tag is found
GONMarkupParserLogLevelWorkflow = 0x01 << 2, // Logs parser workflow (Very verbose)
GONMarkupParserLogLevelFonts = 0x01 << 3, // Logs fonts errors (Very useful to detect missing bold / italic fonts)
GONMarkupParserLogLevelAll = 0xFF, // Logs all
GONMarkupParserLogLevelErrors = GONMarkupParserLogLevelFonts | GONMarkupParserLogLevelUnknownTag | GONMarkupParserLogLevelUnbalancedTags
} GONMarkupParserLogLevel;
@interface GONMarkupParser : NSObject
// Constructors
/* Instanciate a new markup parser, configured to handle all default tags */
+ (GONMarkupParser *)defaultMarkupParser;
/* Instanciate a new empty markup parser. This parser won't have any configured tag
* Same as [[GONMarkupParser alloc] init]
*/
+ (GONMarkupParser *)emptyMarkupParser;
/* Add a new markup to parser
* A markup can be used in only one parser at a time
* If you are trying to a markup already used by another parser, an exception willl be thrown
*/
- (void)addMarkup:(GONMarkup *)markup;
/* Add some markups */
- (void)addMarkups:(id <NSFastEnumeration>)markups;
/* Return a previously registered markup that can handle given tag */
- (GONMarkup *)markupForTag:(NSString *)tag;
/* Remove given markup from parser, if previously added */
- (void)removeMarkup:(GONMarkup *)markup;
/* Remove given markups from parser, if previously added */
- (void)removeMarkups:(id <NSFastEnumeration>)markups;
/* Remove all currently applied markups */
- (void)removeAllMarkups;
// Fonts management
/* Register given font */
- (void)registerFont:(UIFont *)font forKey:(NSString *)key;
/* Register given font */
- (UIFont *)fontForKey:(NSString *)key;
/* Register given font */
- (void)unregisterFontForKey:(NSString *)key;
/* Convert given string to an attributed string.
* String will be parsed and updated according to stored markups
* If nil is passed as parameter, an empty string will be returned
*
* anError may be set if input string seems malformed, but method will return a valid string. Only last error is returned.
*/
- (NSMutableAttributedString *)attributedStringFromString:(NSString *)string error:(NSError **)error;
/* Same as attributedStringFromString:string error:nil.
* Because sometimes, you don't care about errors.
*/
- (NSMutableAttributedString *)attributedStringFromString:(NSString *)string;
@property (nonatomic, assign) BOOL replaceNewLineCharactersFromInputString; // If YES, all [NSCharacterSet newlineCharacterSet] will be replaced by spaces from input string.
// Replacing process will occur AFTER preprocessingBlock, BEFORE parsing.
// Use <br/> and <p> to add new lines. Default is NO.
@property (nonatomic, assign) BOOL replaceHTMLCharactersFromOutputString; // If YES, all HTML entities will be replaced in output string.
// Replacing process will occur BEFORE postProcessingBlock, AFTER parsing.
// Default is YES
@property (nonatomic, strong, readonly) NSMutableDictionary *defaultConfiguration; // Default attributed string configuration
@property (nonatomic, readonly) NSArray *markups; // Retrieve all applied markups
@property (nonatomic, readonly) NSDictionary *registeredFonts; // Retrieve all applied markups
@property (nonatomic, copy) void (^preProcessingBlock)(NSMutableString *inputString); // Block automatically called to preprocess input string
@property (nonatomic, copy) void (^postProcessingBlock)(NSMutableAttributedString *inputString); // Block automatically called to postprocess result attributed string
@property (nonatomic, assign) GONMarkupParserLogLevel logLevel; // Bitmask to select displayed logs
@property (nonatomic, assign) BOOL assertOnError; // Enables assert when an error is generated. Useful when debugging.
@end
================================================
FILE: Classes/GONMarkupParser.m
================================================
//
// GONMarkupParser.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland All rights reserved.
//
#import "GONMarkupParser.h"
#import "GONMarkup+Private.h"
#import "GONMarkupDefaultMarkups.h"
#import "GONMarkupParserUtils.h"
#define MARKUP_REGEX @"(.*?)(<[^>]+>|\\Z)"
#define ATTRIBUTES_REGEX @"([^\\s=]+)\\s*=\\s*('(\\\\'|[^']*')|\"(\\\\\"|[^\"])*\")"
#define LOG_IF_DEBUG(level, msg, ...) do {if (_logLevel & level) { NSLog(@"MarkupParser : %@", [NSString stringWithFormat:msg, ##__VA_ARGS__]); }} while(0)
@interface GONMarkupParser ()
// Style
@property (nonatomic, strong) NSMutableDictionary *defaultConfiguration; // Default attributed string configuration
// Fonts
@property (nonatomic, strong) NSMutableDictionary *dicRegisteredFonts; // Registered fonts
// Data
@property (nonatomic, strong) NSMutableDictionary *dicCurrentMarkup; // Dictionary representation of markups
@property (nonatomic, strong) NSRegularExpression *markupRegex; // Regular expression to extract tokens
@property (nonatomic, strong) NSRegularExpression *attributesRegex; // Attributes regex
// Ephemeral internal data. Used to reduce parameters count in internal methods
@property (nonatomic, strong) NSMutableArray *configurationsStack; // Configurations stack
@property (nonatomic, strong) NSMutableArray *markupsStack; // Markups stack
@property (nonatomic, strong) NSMutableArray *markupAttributesStack; // Markups attributes stack
@property (nonatomic, strong) NSMutableDictionary *currentContext; // Current context
@end
@implementation GONMarkupParser
#pragma mark - Constructor
+ (GONMarkupParser *)defaultMarkupParser
{
GONMarkupParser *parser = [[GONMarkupParser alloc] init];
// WIP
// [parser addMarkup:[GONMarkupImage imageMarkup]];
[parser addMarkup:[GONMarkupItalic italicMarkup]];
[parser addMarkup:[GONMarkupBold boldMarkup]];
[parser addMarkup:[GONMarkupStrong strongMarkup]];
[parser addMarkup:[GONMarkupDec decMarkup]];
[parser addMarkup:[GONMarkupInc incMarkup]];
[parser addMarkup:[GONMarkupAnchor anchorMarkup]];
[parser addMarkup:[GONMarkupFont fontMarkup]];
[parser addMarkup:[GONMarkupColor colorMarkup]];
[parser addMarkup:[GONMarkupLineBreak lineBreakMarkup]];
[parser addMarkup:[GONMarkupReset resetMarkup]];
[parser addMarkup:[GONMarkupParagraph paragraphMarkup]];
[parser addMarkups:[GONMarkupLineStyle allMarkups]];
[parser addMarkups:[GONMarkupTextStyle allMarkups]];
[parser addMarkups:[GONMarkupList allMarkups]];
[parser addMarkups:[GONMarkupAlignment allMarkups]];
return parser;
}
+ (GONMarkupParser *)emptyMarkupParser
{
return [[GONMarkupParser alloc] init];
}
- (id)init
{
if (self = [super init])
{
_markupRegex = [[NSRegularExpression alloc] initWithPattern:MARKUP_REGEX
options:NSRegularExpressionDotMatchesLineSeparators
error:nil];
_attributesRegex = [[NSRegularExpression alloc] initWithPattern:ATTRIBUTES_REGEX
options:NSRegularExpressionDotMatchesLineSeparators
error:nil];
_assertOnError = NO;
_logLevel = GONMarkupParserLogLevelNone;
_defaultConfiguration = [[NSMutableDictionary alloc] init];
_dicCurrentMarkup = [[NSMutableDictionary alloc] init];
_dicRegisteredFonts = [[NSMutableDictionary alloc] init];
_replaceNewLineCharactersFromInputString = NO;
_replaceHTMLCharactersFromOutputString = YES;
}
return self;
}
#pragma mark - Markup management
- (void)addMarkup:(GONMarkup *)markup
{
// Nothing to do if already added to parser
if (markup.parser == self)
return;
if (markup.parser != nil)
@throw @"Error, a Markup can be used by only one parser at a time";
// Bind to parser
markup.parser = self;
[_dicCurrentMarkup setObject:markup
forKey:markup.tag];
}
- (GONMarkup *)markupForTag:(NSString *)tag
{
// Retrieve markup
return [_dicCurrentMarkup objectForKey:tag];
}
- (void)addMarkups:(id <NSFastEnumeration>)markups
{
for (GONMarkup *markup in markups)
[self addMarkup:markup];
}
- (void)removeMarkup:(GONMarkup *)markup
{
GONMarkup *currentMarkup = [_dicCurrentMarkup objectForKey:markup.tag];
if (currentMarkup == markup)
{
// Remove parser link
markup.parser = nil;
[_dicCurrentMarkup removeObjectForKey:markup.tag];
}
}
- (void)removeMarkups:(id <NSFastEnumeration>)markups
{
for (GONMarkup *markup in markups)
[self removeMarkup:markup];
}
- (void)removeAllMarkups
{
for (GONMarkup *markup in [_dicCurrentMarkup allValues])
markup.parser = nil;
[_dicCurrentMarkup removeAllObjects];
}
#pragma mark - Parser
- (NSMutableAttributedString *)attributedStringFromString:(NSString *)string
{
return [self attributedStringFromString:string
error:nil];
}
- (NSMutableAttributedString *)attributedStringFromString:(NSString *)string error:(NSError **)error
{
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Input string :\n%@\n", string);
// Check for nil values
if (!string)
{
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Input string was <nil>, returning empty string");
return [[NSMutableAttributedString alloc] init];
}
// Make input string mutable
NSMutableString *inputString = [string mutableCopy];
// Handle pre processing
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Preprocessing string");
if (_preProcessingBlock)
_preProcessingBlock(inputString);
// Replace new line characters
if (_replaceNewLineCharactersFromInputString)
inputString = [[[inputString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:@" "] mutableCopy];
// Parse input string
NSMutableAttributedString *resString = [self parseString:inputString error:error];
if (error)
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Parsing completed with an error <%@>", *error);
else
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Parsing completed without error");
// Replace html entities
if (_replaceHTMLCharactersFromOutputString)
[GONMarkupParserUtils cleanHTMLEntitiesFromString:resString.mutableString];
// Handle post processing
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Postprocessing string");
if (_postProcessingBlock)
_postProcessingBlock(resString);
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Result string :\n%@\n", resString);
return resString;
}
- (NSMutableAttributedString *)parseString:(NSString *)inputString error:(NSError **)error
{
// Init stack
_configurationsStack = [[NSMutableArray alloc] init];
_currentContext = [[NSMutableDictionary alloc] init];
_markupsStack = [[NSMutableArray alloc] init];
_markupAttributesStack = [[NSMutableArray alloc] init];
// Parse string
NSArray *results = [_markupRegex matchesInString:inputString
options:0
range:NSMakeRange(0, inputString.length)];
// Prepare result string
NSMutableAttributedString *resultString = [[NSMutableAttributedString alloc] init];
// Browse chunks
NSString *tag;
BOOL autoclosingMarkup;
@try
{
[resultString beginEditing];
for (NSTextCheckingResult *result in results)
{
// Split string
NSArray *parts = [[inputString substringWithRange:result.range] componentsSeparatedByString:@"<"];
// Append extracted string
[resultString appendAttributedString:[self computeFinalExtractedString:[parts firstObject]
resultString:resultString]];
// Check if a tag was found
if (parts.count > 1)
{
// Extract tag and clean it
tag = [parts objectAtIndex:1];
tag = [tag substringToIndex:tag.length - 1]; // Remove final >
tag = [tag stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([tag rangeOfString:@"/"].location == 0)
{
// Lowercase tag, trim closing character
tag = [[tag substringFromIndex:1] lowercaseString];
// Trim potential remaining white spaces
tag = [tag stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// Closing current tag, so append result string
[resultString appendAttributedString:[self computeSuffixString:resultString]];
[self handleClosingTag:tag
resultString:resultString
error:error];
}
else
{
// Check if autoclosing markup or not
autoclosingMarkup = [tag rangeOfString:@"/" options:NSBackwardsSearch].location == (tag.length - 1);
// If autoclosing markup, trim last /
if (autoclosingMarkup)
tag = [tag substringToIndex:tag.length - 1];
// Trim potential remaining white spaces
tag = [tag stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// Split tag / attributes
tag = [self extractTagAndPushAttributesFromTag:tag];
// Handle autoclosing markup
if (autoclosingMarkup)
{
// Opening tag
[self handleOpeningTag:tag
resultString:resultString
error:error];
// Append an extracted empty string
[resultString appendAttributedString:[self computePrefixString:resultString]];
[resultString appendAttributedString:[self computeFinalExtractedString:@"" resultString:resultString]];
[resultString appendAttributedString:[self computeSuffixString:resultString]];
// Close tag
[self handleClosingTag:tag
resultString:resultString
error:error];
}
else
{
// Opening tag
[self handleOpeningTag:tag
resultString:resultString
error:error];
[resultString appendAttributedString:[self computePrefixString:resultString]];
}
}
}
}
[resultString endEditing];
if (_configurationsStack.count != 0)
{
LOG_IF_DEBUG(GONMarkupParserLogLevelUnbalancedTags, @"Parsing completed, but stack isn't empty, some closing tags seems missing :\nStack :%@\n", _configurationsStack);
[self generateError:error tag:nil];
}
}
@catch (NSException *exception)
{
LOG_IF_DEBUG(GONMarkupParserLogLevelErrors, @"An error did occur while parsing :%@\n", exception);
LOG_IF_DEBUG(GONMarkupParserLogLevelErrors, @"Parsed string so for :\n%@\n", resultString.string);
[self generateError:error tag:nil];
}
// Flush unuseful data
_markupsStack = nil;
_configurationsStack = nil;
_currentContext = nil;
_markupAttributesStack = nil;
return resultString;
}
#pragma mark - Utils
- (NSString *)extractTagAndPushAttributesFromTag:(NSString *)tag
{
// Check for attributes
NSRange range = [tag rangeOfString:@" "];
NSDictionary *attributes = nil;
NSString *extractedTag;
if (range.location == NSNotFound)
{
// No attributed to extract, and tag is full string
extractedTag = tag;
}
else
{
// There may be some attributes, so extract tag
extractedTag = [tag substringToIndex:range.location];
attributes = [self extractAttributesFromString:[tag substringFromIndex:range.location]];
}
// Check if some attributes were found
[_markupAttributesStack addObject:(attributes.count ? attributes : [NSNull null])];
return [extractedTag lowercaseString];
}
- (NSDictionary *)attributesForCurrentTag
{
id attributes = [_markupAttributesStack lastObject];
if (attributes == [NSNull null])
return nil;
return attributes;
}
- (NSDictionary *)extractAttributesFromString:(NSString *)string
{
NSMutableDictionary *dicAttributes = [[NSMutableDictionary alloc] init];
// Parse string
NSArray *results = [_attributesRegex matchesInString:string
options:0
range:NSMakeRange(0, string.length)];
// Browse chunks
NSString *matchedString;
NSRange range;
NSString *attributeKey;
NSMutableString *attributeValue;
for (NSTextCheckingResult *result in results)
{
// Extract matched string
matchedString = [string substringWithRange:result.range];
// Look for character to split string
range = [matchedString rangeOfString:@"="];
// Extract key
attributeKey = [[matchedString substringToIndex:range.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// Extract value
attributeValue = [[[matchedString substringFromIndex:range.location + 1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
// Clean value, by trimming enclosing quotes / double quotes, cleaning potential " entities
[attributeValue replaceCharactersInRange:NSMakeRange(attributeValue.length - 1, 1) withString:@""];
[attributeValue replaceCharactersInRange:NSMakeRange(0, 1) withString:@""];
[attributeValue replaceOccurrencesOfString:@""" withString:@"\"" options:0 range:NSMakeRange(0, attributeValue.length)];
// Clean pontential html entities in string
[dicAttributes setObject:attributeValue
forKey:attributeKey];
}
return dicAttributes;
}
#pragma mark - Tag content managements
- (NSAttributedString *)computePrefixString:(NSAttributedString *)resultString
{
GONMarkup *currentMarker = [_markupsStack lastObject];
if (currentMarker && ![currentMarker isKindOfClass:[NSNull class]])
{
return [currentMarker prefixStringForContext:_currentContext
attributes:[self attributesForCurrentTag]
stringAttributes:[self currentConfiguration]
resultString:resultString];
}
return [[NSAttributedString alloc] initWithString:@""];
}
- (NSAttributedString *)computeSuffixString:(NSAttributedString *)resultString
{
GONMarkup *currentMarker = [_markupsStack lastObject];
if (currentMarker && ![currentMarker isKindOfClass:[NSNull class]])
{
return [currentMarker suffixStringForContext:_currentContext
attributes:[self attributesForCurrentTag]
stringAttributes:[self currentConfiguration]
resultString:resultString];
}
return [[NSAttributedString alloc] initWithString:@"" attributes:[self currentConfiguration]];
}
- (NSAttributedString *)computeFinalExtractedString:(NSString *)inputString
resultString:(NSAttributedString *)resultString
{
GONMarkup *currentMarker = [_markupsStack lastObject];
if (currentMarker && ![currentMarker isKindOfClass:[NSNull class]])
{
return [currentMarker updatedContentString:inputString
context:_currentContext
attributes:[self attributesForCurrentTag]
stringAttributes:[self currentConfiguration]
resultString:resultString];
}
return [[NSAttributedString alloc] initWithString:inputString attributes:[self currentConfiguration]];
}
#pragma mark - Tag managements
- (BOOL)handleClosingTag:(NSString *)tag
resultString:(NSAttributedString *)resultString
error:(NSError **)error
{
// Hold error status
BOOL errorGenerated = NO;
// Look for full style closing tag, @"//"
// Checking only for one /, because first one was trimmed
if ([tag rangeOfString:@"/"].location == 0)
{
if (!_configurationsStack.count)
{
LOG_IF_DEBUG(GONMarkupParserLogLevelUnbalancedTags, @"Trying to close all tags, but stack is empty");
errorGenerated = [self generateError:error tag:tag];
}
else
{
GONMarkup *markup;
NSMutableDictionary *currentTagConfiguration;
for (NSInteger i=_markupsStack.count - 1; i>=0; i--)
{
markup = [_markupsStack objectAtIndex:i];
currentTagConfiguration = [_configurationsStack objectAtIndex:i];
// If we are closing an unknown tag, skip it
if (![markup isKindOfClass:[NSNull class]])
{
[markup closingMarkupFound:tag
configuration:currentTagConfiguration
context:_currentContext
attributes:([_markupAttributesStack objectAtIndex:i] == [NSNull null] ? nil : [_markupAttributesStack objectAtIndex:i])
resultString:resultString];
}
else
{
LOG_IF_DEBUG(GONMarkupParserLogLevelUnknownTag, @"Closing unkown found tag <%@>", tag);
errorGenerated = [self generateError:error tag:tag];
}
}
[_configurationsStack removeAllObjects];
[_markupsStack removeAllObjects];
[_currentContext removeAllObjects];
[_markupAttributesStack removeAllObjects];
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Closing all tags\nStack : %@\n", _configurationsStack);
}
}
else
{
// Check if available tags
if (!_configurationsStack.count)
{
// Closing a tag, but tags stack is empty
LOG_IF_DEBUG(GONMarkupParserLogLevelUnbalancedTags, @"Trying to close last tag, but stack is empty");
errorGenerated = [self generateError:error tag:tag];
}
else
{
// Extract current markup
GONMarkup *markup = [_markupsStack lastObject];
// Present error when closing an unkwnow markup
if (markup && ![markup isKindOfClass:[NSNull class]])
{
// Check that closing that is matching opening one
if (tag.length)
{
if (![tag isEqualToString:markup.tag])
{
LOG_IF_DEBUG(GONMarkupParserLogLevelUnbalancedTags, @"Closing tag found <%@>, is not matching currently opened one <%@>", tag, markup.tag);
errorGenerated = [self generateError:error tag:tag];
}
}
[markup closingMarkupFound:tag
configuration:[_configurationsStack lastObject]
context:_currentContext
attributes:[self attributesForCurrentTag]
resultString:resultString];
}
// Remove last tag objet
[_configurationsStack removeLastObject];
[_markupsStack removeLastObject];
[_markupAttributesStack removeLastObject];
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Closing tag (%@)\nStack : %@\n", tag, _configurationsStack);
}
}
return errorGenerated;
}
- (BOOL)handleOpeningTag:(NSString *)tag
resultString:(NSAttributedString *)resultString
error:(NSError **)error
{
// Hold error status
BOOL errorGenerated = NO;
// Prepare tag configuration
NSMutableDictionary *currentTagConfiguration = [self mutableCurrentConfiguration];
// Retrieve markup associated to tag
GONMarkup *markup = [self markupForTag:tag];
// Ensure a markup was found
if (!markup)
{
LOG_IF_DEBUG(GONMarkupParserLogLevelUnknownTag, @"No markup found for tag <%@>\n", tag);
errorGenerated = [self generateError:error
tag:tag];
[_markupsStack addObject:[NSNull null]];
}
else
{
[markup openingMarkupFound:tag
configuration:currentTagConfiguration
context:_currentContext
attributes:[self attributesForCurrentTag]
resultString:resultString];
[_markupsStack addObject:markup];
}
// Hold configuration
[_configurationsStack addObject:currentTagConfiguration];
LOG_IF_DEBUG(GONMarkupParserLogLevelWorkflow, @"Opening tag (%@)\nStack : %@\n", tag, _configurationsStack);
return errorGenerated;
}
- (NSDictionary *)currentConfiguration
{
// Extract configuration on top of stack
if (!_configurationsStack.count)
return _defaultConfiguration;
return [_configurationsStack lastObject];
}
- (NSMutableDictionary *)mutableCurrentConfiguration
{
NSMutableDictionary *mutableConfiguration = [[self currentConfiguration] mutableCopy];
// Force paragraph style mutability
NSParagraphStyle *paragraphStyle = [mutableConfiguration objectForKey:NSParagraphStyleAttributeName];
if (paragraphStyle)
{
[mutableConfiguration setObject:[paragraphStyle mutableCopy]
forKey:NSParagraphStyleAttributeName];
}
return mutableConfiguration;
}
#pragma mark - Fonts management
- (void)registerFont:(UIFont *)font forKey:(NSString *)key
{
[_dicRegisteredFonts setObject:font
forKey:key];
}
- (UIFont *)fontForKey:(NSString *)key
{
return [_dicRegisteredFonts objectForKey:key];
}
- (void)unregisterFontForKey:(NSString *)key
{
[_dicRegisteredFonts removeObjectForKey:key];
}
#pragma mark - Error handling
- (BOOL)generateError:(NSError **)error tag:(NSString *)tag
{
// Assert only is requested
if (_assertOnError)
NSAssert1(NO, @"An error was generated parsing following text, found at markup <%@>", tag);
if (error)
{
// Initialize user info
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:@"Input string is malformed. Output attributed string may not be displayed correctly"
forKey:NSLocalizedDescriptionKey];
// Add tag if avalaible
if (tag)
{
[userInfo setObject:tag
forKey:GONMarkupParser_incorrectClosingTag_KEY];
}
// Build error
*error = [NSError errorWithDomain:GONMarkupParser_ERROR_DOMAIN
code:GONMarkupParser_StringMalformed_ERROR_CODE
userInfo:userInfo];
return YES;
}
return NO;
}
#pragma mark - Getters
- (NSArray *)markups
{
return [[_dicCurrentMarkup allValues] copy];
}
- (NSDictionary *)registeredFonts
{
return [_dicRegisteredFonts copy];
}
@end
================================================
FILE: Classes/GONMarkupParserManager.h
================================================
//
// GONMarkupParserManager.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/07/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// This class helps you to manage shared parsers
// It also provides a default shared parser for your application
#import "GONMarkupParser.h"
@interface GONMarkupParserManager : NSObject
// Shared parsers management
/* Return shared parser instance.
* Shared parser is preconfigured to handle all default tags
*/
+ (GONMarkupParser *)sharedParser;
/* Retrieve a parser registered under given key
* Return nil if not found
*/
+ (GONMarkupParser *)registeredMarkupParserForKey:(NSString *)key;
/* Register given parser under key
* Use this method to be able to share a parser
*/
+ (void)registerMarkupParser:(GONMarkupParser *)parser forKey:(NSString *)key;
/* Unregister parser under given key */
+ (void)unregisterMarkupParserForKey:(NSString *)key;
@end
================================================
FILE: Classes/GONMarkupParserManager.m
================================================
//
// GONMarkupParserManager.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/07/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParserManager.h"
#import "GONMarkupDefaultMarkups.h"
@interface GONMarkupParserManager ()
// Data
@property (nonatomic, strong) NSMutableDictionary *registeredParsers;
@end
@implementation GONMarkupParserManager
#pragma mark - Shared manager
+ (instancetype)manager
{
static GONMarkupParserManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init
{
if (self = [super init])
{
_registeredParsers = [[NSMutableDictionary alloc] init];
}
return self;
}
#pragma mark - Shared instance
+ (GONMarkupParser *)sharedParser
{
static GONMarkupParser *sharedParser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedParser = [GONMarkupParser defaultMarkupParser];
});
return sharedParser;
}
+ (GONMarkupParser *)registeredMarkupParserForKey:(NSString *)key
{
return [[[self manager] registeredParsers] objectForKey:key];
}
+ (void)registerMarkupParser:(GONMarkupParser *)parser forKey:(NSString *)key
{
[[[self manager] registeredParsers] setObject:parser
forKey:key];
}
+ (void)unregisterMarkupParserForKey:(NSString *)key
{
[[[self manager] registeredParsers] removeObjectForKey:key];
}
@end
================================================
FILE: Classes/GONMarkupParserUtils.h
================================================
//
// GONMarkupParserUtils.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/08/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Utils
// Why not using some categories Sir ? Because I'm pretty sure everyone already have a cleanHTMLEntities method on their strings,
// and I don't want to conflict with existing methods, nor adding a pod dependency that will add tons of new methods
@class GONMarkupParser;
@interface GONMarkupParserUtils : NSObject
/* Clean HTML entities from string */
+ (void)cleanHTMLEntitiesFromString:(NSMutableString *)inputString;
/* Helper method to generate an attributed string from a parser, but allowing to set default text color, text alignment and font
* if they are not set in parser default configuration.
* This mthod will update parser default configuration, generate string, then revert configuration back to previous state
* Used in UILAbel+GONMarkupParser and UITextField+GONMarkupParser categories to prevent duplicated code
* If parser is nil, [GONMarkupParserManager sharedParser] will be used
*/
+ (NSAttributedString *)attributedString:(GONMarkupParser *)parser
withString:(NSString *)inputString
defautColor:(UIColor *)defaultColor
defaultFont:(UIFont *)defaultFont
defaultAlignment:(NSTextAlignment)defaultAlignment;
@end
================================================
FILE: Classes/GONMarkupParserUtils.m
================================================
//
// GONMarkupParserUtils.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/08/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// http://www.w3schools.com/charsets/ref_html_entities_4.asp
#import "GONMarkupParserUtils.h"
#import "GONMarkupParserManager.h"
#define HTML_ENTITY_REGEX @"&#?[a-zA-Z0-9]+;"
#define HTML_ENTITY_256_PREFIX_SEARCH @"�"
#define HTML_ENTITY_256_PREFIX_REPLACE @"&#"
#define HTML_ENTITY_256_LENGTH 6
@implementation GONMarkupParserUtils
#pragma mark - Utils
+ (void)cleanHTMLEntitiesFromString:(NSMutableString *)inputString
{
static NSRegularExpression *regex = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
regex = [NSRegularExpression regularExpressionWithPattern:HTML_ENTITY_REGEX
options:0
error:nil];
});
NSRange range = NSMakeRange(0, inputString.length);
NSTextCheckingResult *result = nil;
NSString *entity = nil;
NSString * match = nil;
while ((result = [regex firstMatchInString:inputString options:0 range:range]) != nil) {
// Check if entity is known
match = [inputString substringWithRange:result.range];
entity = [dicEntities objectForKey:match];
// If no entity found, try with alternate synthax
if (!entity)
{
// Check only if
if (match.length == HTML_ENTITY_256_LENGTH && [match rangeOfString:HTML_ENTITY_256_PREFIX_SEARCH].location == 0)
{
match = [match stringByReplacingOccurrencesOfString:HTML_ENTITY_256_PREFIX_SEARCH withString:HTML_ENTITY_256_PREFIX_REPLACE];
// Look again for entity
entity = [dicEntities objectForKey:match];
}
}
if (entity)
[inputString replaceCharactersInRange:result.range withString:entity];
// Update range
range = NSMakeRange(result.range.location + 1, inputString.length - result.range.location - 1);
// Break if reaching end of string
if (range.location + range.length > inputString.length)
break;
};
}
+ (NSAttributedString *)attributedString:(GONMarkupParser *)parser
withString:(NSString *)inputString
defautColor:(UIColor *)defaultColor
defaultFont:(UIFont *)defaultFont
defaultAlignment:(NSTextAlignment)defaultAlignment
{
GONMarkupParser *selectedParser = parser;
if (!selectedParser)
selectedParser = [GONMarkupParserManager sharedParser];
// Hold initial default configuration
NSDictionary *defaultConfiguration = [selectedParser.defaultConfiguration copy];
// Check if parser has default configuration for color / font / alignment. If not, use default configuration
// Text color
if (![selectedParser.defaultConfiguration objectForKey:NSForegroundColorAttributeName])
{
[selectedParser.defaultConfiguration setObject:(defaultColor ? defaultColor : [UIColor blackColor])
forKey:NSForegroundColorAttributeName];
}
// Font
if (![selectedParser.defaultConfiguration objectForKey:NSFontAttributeName] && defaultFont)
{
[selectedParser.defaultConfiguration setObject:defaultFont
forKey:NSFontAttributeName];
}
// Alignment
if (![selectedParser.defaultConfiguration objectForKey:NSParagraphStyleAttributeName])
{
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.alignment = defaultAlignment;
[selectedParser.defaultConfiguration setObject:paragraphStyle
forKey:NSParagraphStyleAttributeName];
}
// Compute attributed string
NSAttributedString *attributedString = [selectedParser attributedStringFromString:inputString
error:nil];
// Reset default configuration
[selectedParser.defaultConfiguration removeAllObjects];
[selectedParser.defaultConfiguration addEntriesFromDictionary:defaultConfiguration];
return attributedString;
}
#pragma mark - Config
static NSDictionary *dicEntities;
+ (void)initialize
{
[super initialize];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dicEntities = @{
@""" : @"\"",
@"&" : @"&",
@"'" : @"'",
@"<" : @"<",
@">" : @">",
@" " : @"\u00A0",
@"¡" : @"\u00A1",
@"¢" : @"\u00A2",
@"£" : @"\u00A3",
@"¤" : @"\u00A4",
@"¥" : @"\u00A5",
@"¦" : @"\u00A6",
@"§" : @"\u00A7",
@"¨" : @"\u00A8",
@"©" : @"\u00A9",
@"ª" : @"\u00AA",
@"«" : @"\u00AB",
@"¬" : @"\u00AC",
@"­" : @"\u00AD",
@"®" : @"\u00AE",
@"¯" : @"\u00AF",
@"°" : @"\u00B0",
@"±" : @"\u00B1",
@"²" : @"\u00B2",
@"³" : @"\u00B3",
@"´" : @"\u00B4",
@"µ" : @"\u00B5",
@"¶" : @"\u00B6",
@"·" : @"\u00B7",
@"¸" : @"\u00B8",
@"¹" : @"\u00B9",
@"º" : @"\u00BA",
@"»" : @"\u00BB",
@"¼" : @"\u00BC",
@"½" : @"\u00BD",
@"¾" : @"\u00BE",
@"¿" : @"\u00BF",
@"À" : @"\u00C0",
@"Á" : @"\u00C1",
@"Â" : @"\u00C2",
@"Ã" : @"\u00C3",
@"Ä" : @"\u00C4",
@"Å" : @"\u00C5",
@"Æ" : @"\u00C6",
@"Ç" : @"\u00C7",
@"È" : @"\u00C8",
@"É" : @"\u00C9",
@"Ê" : @"\u00CA",
@"Ë" : @"\u00CB",
@"Ì" : @"\u00CC",
@"Í" : @"\u00CD",
@"Î" : @"\u00CE",
@"Ï" : @"\u00CF",
@"Ð" : @"\u00D0",
@"Ñ" : @"\u00D1",
@"Ò" : @"\u00D2",
@"Ó" : @"\u00D3",
@"Ô" : @"\u00D4",
@"Õ" : @"\u00D5",
@"Ö" : @"\u00D6",
@"×" : @"\u00D7",
@"Ø" : @"\u00D8",
@"Ù" : @"\u00D9",
@"Ú" : @"\u00DA",
@"Û" : @"\u00DB",
@"Ü" : @"\u00DC",
@"Ý" : @"\u00DD",
@"Þ" : @"\u00DE",
@"ß" : @"\u00DF",
@"à" : @"\u00E0",
@"á" : @"\u00E1",
@"â" : @"\u00E2",
@"ã" : @"\u00E3",
@"ä" : @"\u00E4",
@"å" : @"\u00E5",
@"æ" : @"\u00E6",
@"ç" : @"\u00E7",
@"è" : @"\u00E8",
@"é" : @"\u00E9",
@"ê" : @"\u00EA",
@"ë" : @"\u00EB",
@"ì" : @"\u00EC",
@"í" : @"\u00ED",
@"î" : @"\u00EE",
@"ï" : @"\u00EF",
@"ð" : @"\u00F0",
@"ñ" : @"\u00F1",
@"ò" : @"\u00F2",
@"ó" : @"\u00F3",
@"ô" : @"\u00F4",
@"õ" : @"\u00F5",
@"ö" : @"\u00F6",
@"÷" : @"\u00F7",
@"ø" : @"\u00F8",
@"ù" : @"\u00F9",
@"ú" : @"\u00FA",
@"û" : @"\u00FB",
@"ü" : @"\u00FC",
@"ý" : @"\u00FD",
@"þ" : @"\u00FE",
@"ÿ" : @"\u00FF",
@"Œ" : @"\u0152",
@"œ" : @"\u0153",
@"Š" : @"\u0160",
@"š" : @"\u0161",
@"Ÿ" : @"\u0178",
@"ƒ" : @"\u0192",
@"ˆ" : @"\u02C6",
@"˜" : @"\u02DC",
@"Α" : @"\u0391",
@"Β" : @"\u0392",
@"Γ" : @"\u0393",
@"Δ" : @"\u0394",
@"Ε" : @"\u0395",
@"Ζ" : @"\u0396",
@"Η" : @"\u0397",
@"Θ" : @"\u0398",
@"Ι" : @"\u0399",
@"Κ" : @"\u039A",
@"Λ" : @"\u039B",
@"Μ" : @"\u039C",
@"Ν" : @"\u039D",
@"Ξ" : @"\u039E",
@"Ο" : @"\u039F",
@"Π" : @"\u03A0",
@"Ρ" : @"\u03A1",
@"Σ" : @"\u03A3",
@"Τ" : @"\u03A4",
@"Υ" : @"\u03A5",
@"Φ" : @"\u03A6",
@"Χ" : @"\u03A7",
@"Ψ" : @"\u03A8",
@"Ω" : @"\u03A9",
@"α" : @"\u03B1",
@"β" : @"\u03B2",
@"γ" : @"\u03B3",
@"δ" : @"\u03B4",
@"ε" : @"\u03B5",
@"ζ" : @"\u03B6",
@"η" : @"\u03B7",
@"θ" : @"\u03B8",
@"ι" : @"\u03B9",
@"κ" : @"\u03BA",
@"λ" : @"\u03BB",
@"μ" : @"\u03BC",
@"ν" : @"\u03BD",
@"ξ" : @"\u03BE",
@"ο" : @"\u03BF",
@"π" : @"\u03C0",
@"ρ" : @"\u03C1",
@"ς" : @"\u03C2",
@"σ" : @"\u03C3",
@"τ" : @"\u03C4",
@"υ" : @"\u03C5",
@"φ" : @"\u03C6",
@"χ" : @"\u03C7",
@"ψ" : @"\u03C8",
@"ω" : @"\u03C9",
@"ϑ" : @"\u03D1",
@"ϒ" : @"\u03D2",
@"ϖ" : @"\u03D6",
@" " : @"\u2002",
@" " : @"\u2003",
@" " : @"\u2009",
@"‌" : @"\u200C",
@"‍" : @"\u200D",
@"‎" : @"\u200E",
@"‏" : @"\u200F",
@"–" : @"\u2013",
@"—" : @"\u2014",
@"‘" : @"\u2018",
@"’" : @"\u2019",
@"‚" : @"\u201A",
@"“" : @"\u201C",
@"”" : @"\u201D",
@"„" : @"\u201E",
@"†" : @"\u2020",
@"‡" : @"\u2021",
@"•" : @"\u2022",
@"…" : @"\u2026",
@"‰" : @"\u2030",
@"′" : @"\u2032",
@"″" : @"\u2033",
@"‹" : @"\u2039",
@"›" : @"\u203A",
@"‾" : @"\u203E",
@"⁄" : @"\u2044",
@"€" : @"\u20AC",
@"ℑ" : @"\u2111",
@"℘" : @"\u2118",
@"ℜ" : @"\u211C",
@"™" : @"\u2122",
@"ℵ" : @"\u2135",
@"←" : @"\u2190",
@"↑" : @"\u2191",
@"→" : @"\u2192",
@"↓" : @"\u2193",
@"↔" : @"\u2194",
@"↵" : @"\u21B5",
@"⇐" : @"\u21D0",
@"⇑" : @"\u21D1",
@"⇒" : @"\u21D2",
@"⇓" : @"\u21D3",
@"⇔" : @"\u21D4",
@"∀" : @"\u2200",
@"∂" : @"\u2202",
@"∃" : @"\u2203",
@"∅" : @"\u2205",
@"∇" : @"\u2207",
@"∈" : @"\u2208",
@"∉" : @"\u2209",
@"∋" : @"\u220B",
@"∏" : @"\u220F",
@"∑" : @"\u2211",
@"−" : @"\u2212",
@"∗" : @"\u2217",
@"√" : @"\u221A",
@"∝" : @"\u221D",
@"∞" : @"\u221E",
@"∠" : @"\u2220",
@"∧" : @"\u2227",
@"∨" : @"\u2228",
@"∩" : @"\u2229",
@"∪" : @"\u222A",
@"∫" : @"\u222B",
@"∴" : @"\u2234",
@"∼" : @"\u223C",
@"≅" : @"\u2245",
@"≈" : @"\u2248",
@"≠" : @"\u2260",
@"≡" : @"\u2261",
@"≤" : @"\u2264",
@"≥" : @"\u2265",
@"⊂" : @"\u2282",
@"⊃" : @"\u2283",
@"⊄" : @"\u2284",
@"⊆" : @"\u2286",
@"⊇" : @"\u2287",
@"⊕" : @"\u2295",
@"⊗" : @"\u2297",
@"⊥" : @"\u22A5",
@"⋅" : @"\u22C5",
@"⋮" : @"\u22EE",
@"⌈" : @"\u2308",
@"⌉" : @"\u2309",
@"⌊" : @"\u230A",
@"⌋" : @"\u230B",
@"⟨" : @"\u2329",
@"⟩" : @"\u232A",
@"◊" : @"\u25CA",
@"♠" : @"\u2660",
@"♣" : @"\u2663",
@"♥" : @"\u2665",
@"♦" : @"\u2666",
@" " : @" ",
@"!" : @"!",
@""" : @"\"",
@"#" : @"#",
@"$" : @"$",
@"%" : @"%",
@"&" : @"&",
@"'" : @"'",
@"(" : @"(",
@")" : @")",
@"*" : @"*",
@"+" : @"+",
@"," : @",",
@"-" : @"-",
@"." : @".",
@"/" : @"/",
@"0" : @"0",
@"1" : @"1",
@"2" : @"2",
@"3" : @"3",
@"4" : @"4",
@"5" : @"5",
@"6" : @"6",
@"7" : @"7",
@"8" : @"8",
@"9" : @"9",
@":" : @":",
@";" : @";",
@"<" : @"<",
@"=" : @"=",
@">" : @">",
@"?" : @"?",
@"@" : @"@",
@"A" : @"A",
@"B" : @"B",
@"C" : @"C",
@"D" : @"D",
@"E" : @"E",
@"F" : @"F",
@"G" : @"G",
@"H" : @"H",
@"I" : @"I",
@"J" : @"J",
@"K" : @"K",
@"L" : @"L",
@"M" : @"M",
@"N" : @"N",
@"O" : @"O",
@"P" : @"P",
@"Q" : @"Q",
@"R" : @"R",
@"S" : @"S",
@"T" : @"T",
@"U" : @"U",
@"V" : @"V",
@"W" : @"W",
@"X" : @"X",
@"Y" : @"Y",
@"Z" : @"Z",
@"[" : @"[",
@"\" : @"\\",
@"]" : @"]",
@"^" : @"^",
@"_" : @"_",
@"`" : @"`",
@"a" : @"a",
@"b" : @"b",
@"c" : @"c",
@"d" : @"d",
@"e" : @"e",
@"f" : @"f",
@"g" : @"g",
@"h" : @"h",
@"i" : @"i",
@"j" : @"j",
@"k" : @"k",
@"l" : @"l",
@"m" : @"m",
@"n" : @"n",
@"o" : @"o",
@"p" : @"p",
@"q" : @"q",
@"r" : @"r",
@"s" : @"s",
@"t" : @"t",
@"u" : @"u",
@"v" : @"v",
@"w" : @"w",
@"x" : @"x",
@"y" : @"y",
@"z" : @"z",
@"{" : @"{",
@"|" : @"|",
@"}" : @"}",
@"~" : @"~",
@"" : @"\u0127",
@"€" : @"\u0128",
@"" : @"\u0129",
@"‚" : @"\u0130",
@"ƒ" : @"\u0131",
@"„" : @"\u0132",
@"…" : @"\u0133",
@"†" : @"\u0134",
@"‡" : @"\u0135",
@"ˆ" : @"\u0136",
@"‰" : @"\u0137",
@"Š" : @"\u0138",
@"‹" : @"\u0139",
@"Œ" : @"\u0140",
@"" : @"\u0141",
@"Ž" : @"\u0142",
@"" : @"\u0143",
@"" : @"\u0144",
@"‘" : @"\u0145",
@"’" : @"\u0146",
@"“" : @"\u0147",
@"”" : @"\u0148",
@"•" : @"\u0149",
@"–" : @"\u0150",
@"—" : @"\u0151",
@"˜" : @"\u0152",
@"™" : @"\u0153",
@"š" : @"\u0154",
@"›" : @"\u0155",
@"œ" : @"\u0156",
@"" : @"\u0157",
@"ž" : @"\u0158",
@"Ÿ" : @"\u0159",
@" " : @"\u00A0",
@"¡" : @"\u00A1",
@"¢" : @"\u00A2",
@"£" : @"\u00A3",
@"¤" : @"\u00A4",
@"¥" : @"\u00A5",
@"¦" : @"\u00A6",
@"§" : @"\u00A7",
@"¨" : @"\u00A8",
@"©" : @"\u00A9",
@"ª" : @"\u00AA",
@"«" : @"\u00AB",
@"¬" : @"\u00AC",
@"­" : @"\u00AD",
@"®" : @"\u00AE",
@"¯" : @"\u00AF",
@"°" : @"\u00B0",
@"±" : @"\u00B1",
@"²" : @"\u00B2",
@"³" : @"\u00B3",
@"´" : @"\u00B4",
@"µ" : @"\u00B5",
@"¶" : @"\u00B6",
@"·" : @"\u00B7",
@"¸" : @"\u00B8",
@"¹" : @"\u00B9",
@"º" : @"\u00BA",
@"»" : @"\u00BB",
@"¼" : @"\u00BC",
@"½" : @"\u00BD",
@"¾" : @"\u00BE",
@"¿" : @"\u00BF",
@"À" : @"\u00C0",
@"Á" : @"\u00C1",
@"Â" : @"\u00C2",
@"Ã" : @"\u00C3",
@"Ä" : @"\u00C4",
@"Å" : @"\u00C5",
@"Æ" : @"\u00C6",
@"Ç" : @"\u00C7",
@"È" : @"\u00C8",
@"É" : @"\u00C9",
@"Ê" : @"\u00CA",
@"Ë" : @"\u00CB",
@"Ì" : @"\u00CC",
@"Í" : @"\u00CD",
@"Î" : @"\u00CE",
@"Ï" : @"\u00CF",
@"Ð" : @"\u00D0",
@"Ñ" : @"\u00D1",
@"Ò" : @"\u00D2",
@"Ó" : @"\u00D3",
@"Ô" : @"\u00D4",
@"Õ" : @"\u00D5",
@"Ö" : @"\u00D6",
@"×" : @"\u00D7",
@"Ø" : @"\u00D8",
@"Ù" : @"\u00D9",
@"Ú" : @"\u00DA",
@"Û" : @"\u00DB",
@"Ü" : @"\u00DC",
@"Ý" : @"\u00DD",
@"Þ" : @"\u00DE",
@"ß" : @"\u00DF",
@"à" : @"\u00E0",
@"á" : @"\u00E1",
@"â" : @"\u00E2",
@"ã" : @"\u00E3",
@"ä" : @"\u00E4",
@"å" : @"\u00E5",
@"æ" : @"\u00E6",
@"ç" : @"\u00E7",
@"è" : @"\u00E8",
@"é" : @"\u00E9",
@"ê" : @"\u00EA",
@"ë" : @"\u00EB",
@"ì" : @"\u00EC",
@"í" : @"\u00ED",
@"î" : @"\u00EE",
@"ï" : @"\u00EF",
@"ð" : @"\u00F0",
@"ñ" : @"\u00F1",
@"ò" : @"\u00F2",
@"ó" : @"\u00F3",
@"ô" : @"\u00F4",
@"õ" : @"\u00F5",
@"ö" : @"\u00F6",
@"÷" : @"\u00F7",
@"ø" : @"\u00F8",
@"ù" : @"\u00F9",
@"ú" : @"\u00FA",
@"û" : @"\u00FB",
@"ü" : @"\u00FC",
@"ý" : @"\u00FD",
@"þ" : @"\u00FE",
@"ÿ" : @"\u00FF",
@"ƒ" : @"\u0192",
@"Α" : @"\u0391",
@"Β" : @"\u0392",
@"Γ" : @"\u0393",
@"Δ" : @"\u0394",
@"Ε" : @"\u0395",
@"Ζ" : @"\u0396",
@"Η" : @"\u0397",
@"Θ" : @"\u0398",
@"Ι" : @"\u0399",
@"Κ" : @"\u039A",
@"Λ" : @"\u039B",
@"Μ" : @"\u039C",
@"Ν" : @"\u039D",
@"Ξ" : @"\u039E",
@"Ο" : @"\u039F",
@"Π" : @"\u03A0",
@"Ρ" : @"\u03A1",
@"Σ" : @"\u03A3",
@"Τ" : @"\u03A4",
@"Υ" : @"\u03A5",
@"Φ" : @"\u03A6",
@"Χ" : @"\u03A7",
@"Ψ" : @"\u03A8",
@"Ω" : @"\u03A9",
@"α" : @"\u03B1",
@"β" : @"\u03B2",
@"γ" : @"\u03B3",
@"δ" : @"\u03B4",
@"ε" : @"\u03B5",
@"ζ" : @"\u03B6",
@"η" : @"\u03B7",
@"θ" : @"\u03B8",
@"ι" : @"\u03B9",
@"κ" : @"\u03BA",
@"λ" : @"\u03BB",
@"μ" : @"\u03BC",
@"ν" : @"\u03BD",
@"ξ" : @"\u03BE",
@"ο" : @"\u03BF",
@"π" : @"\u03C0",
@"ρ" : @"\u03C1",
@"ς" : @"\u03C2",
@"σ" : @"\u03C3",
@"τ" : @"\u03C4",
@"υ" : @"\u03C5",
@"φ" : @"\u03C6",
@"χ" : @"\u03C7",
@"ψ" : @"\u03C8",
@"ω" : @"\u03C9",
@"ϑ" : @"\u03D1",
@"ϒ" : @"\u03D2",
@"ϖ" : @"\u03D6",
@"•" : @"\u2022",
@"…" : @"\u2026",
@"′" : @"\u2032",
@"″" : @"\u2033",
@"‾" : @"\u203E",
@"⁄" : @"\u2044",
@"℘" : @"\u2118",
@"ℑ" : @"\u2111",
@"ℜ" : @"\u211C",
@"™" : @"\u2122",
@"ℵ" : @"\u2135",
@"←" : @"\u2190",
@"↑" : @"\u2191",
@"→" : @"\u2192",
@"↓" : @"\u2193",
@"↔" : @"\u2194",
@"↵" : @"\u21B5",
@"⇐" : @"\u21D0",
@"⇑" : @"\u21D1",
@"⇒" : @"\u21D2",
@"⇓" : @"\u21D3",
@"⇔" : @"\u21D4",
@"∀" : @"\u2200",
@"∂" : @"\u2202",
@"∃" : @"\u2203",
@"∅" : @"\u2205",
@"∇" : @"\u2207",
@"∈" : @"\u2208",
@"∉" : @"\u2209",
@"∋" : @"\u220B",
@"∏" : @"\u220F",
@"∑" : @"\u2211",
@"−" : @"\u2212",
@"∗" : @"\u2217",
@"√" : @"\u221A",
@"∝" : @"\u221D",
@"∞" : @"\u221E",
@"∠" : @"\u2220",
@"∧" : @"\u2227",
@"∨" : @"\u2228",
@"∩" : @"\u2229",
@"∪" : @"\u222A",
@"∫" : @"\u222B",
@"∴" : @"\u2234",
@"∼" : @"\u223C",
@"≅" : @"\u2245",
@"≈" : @"\u2248",
@"≠" : @"\u2260",
@"≡" : @"\u2261",
@"≤" : @"\u2264",
@"≥" : @"\u2265",
@"⊂" : @"\u2282",
@"⊃" : @"\u2283",
@"⊄" : @"\u2284",
@"⊆" : @"\u2286",
@"⊇" : @"\u2287",
@"⊕" : @"\u2295",
@"⊗" : @"\u2297",
@"⊥" : @"\u22A5",
@"⋅" : @"\u22C5",
@"⌈" : @"\u2308",
@"⌉" : @"\u2309",
@"⌊" : @"\u230A",
@"⌋" : @"\u230B",
@"〈" : @"\u2329",
@"〉" : @"\u232A",
@"◊" : @"\u25CA",
@"♠" : @"\u2660",
@"♣" : @"\u2663",
@"♥" : @"\u2665",
@"♦" : @"\u2666",
@" " : @" ",
@"!" : @"!",
@""" : @"\"",
@"#" : @"#",
@"$" : @"$",
@"%" : @"%",
@"&" : @"&",
@"'" : @"'",
@"(" : @"(",
@")" : @")",
@"*" : @"*",
@"+" : @"+",
@"," : @",",
@"-" : @"-",
@"." : @".",
@"/" : @"/",
@"0" : @"0",
@"1" : @"1",
@"2" : @"2",
@"3" : @"3",
@"4" : @"4",
@"5" : @"5",
@"6" : @"6",
@"7" : @"7",
@"8" : @"8",
@"9" : @"9",
@":" : @":",
@";" : @";",
@"<" : @"<",
@"=" : @"=",
@">" : @">",
@"?" : @"?",
@"@" : @"@",
@"A" : @"A",
@"B" : @"B",
@"C" : @"C",
@"D" : @"D",
@"E" : @"E",
@"F" : @"F",
@"G" : @"G",
@"H" : @"H",
@"I" : @"I",
@"J" : @"J",
@"K" : @"K",
@"L" : @"L",
@"M" : @"M",
@"N" : @"N",
@"O" : @"O",
@"P" : @"P",
@"Q" : @"Q",
@"R" : @"R",
@"S" : @"S",
@"T" : @"T",
@"U" : @"U",
@"V" : @"V",
@"W" : @"W",
@"X" : @"X",
@"Y" : @"Y",
@"Z" : @"Z",
@"[" : @"[",
@"\" : @"\\",
@"]" : @"]",
@"^" : @"^",
@"_" : @"_",
@"`" : @"`",
@"a" : @"a",
@"b" : @"b",
@"c" : @"c",
@"d" : @"d",
@"e" : @"e",
@"f" : @"f",
@"g" : @"g",
@"h" : @"h",
@"i" : @"i",
@"j" : @"j",
@"k" : @"k",
@"l" : @"l",
@"m" : @"m",
@"n" : @"n",
@"o" : @"o",
@"p" : @"p",
@"q" : @"q",
@"r" : @"r",
@"s" : @"s",
@"t" : @"t",
@"u" : @"u",
@"v" : @"v",
@"w" : @"w",
@"x" : @"x",
@"y" : @"y",
@"z" : @"z",
@"{" : @"{",
@"|" : @"|",
@"}" : @"}",
@"~" : @"~",
@"" : @"\u0127",
@"€" : @"\u0128",
@"" : @"\u0129",
@"‚" : @"\u0130",
@"ƒ" : @"\u0131",
@"„" : @"\u0132",
@"…" : @"\u0133",
@"†" : @"\u0134",
@"‡" : @"\u0135",
@"ˆ" : @"\u0136",
@"‰" : @"\u0137",
@"Š" : @"\u0138",
@"‹" : @"\u0139",
@"Œ" : @"\u0140",
@"" : @"\u0141",
@"Ž" : @"\u0142",
@"" : @"\u0143",
@"" : @"\u0144",
@"‘" : @"\u0145",
@"’" : @"\u0146",
@"“" : @"\u0147",
@"”" : @"\u0148",
@"•" : @"\u0149",
@"–" : @"\u0150",
@"—" : @"\u0151",
@"˜" : @"\u0152",
@"™" : @"\u0153",
@"š" : @"\u0154",
@"›" : @"\u0155",
@"œ" : @"\u0156",
@"" : @"\u0157",
@"ž" : @"\u0158",
@"Ÿ" : @"\u0159",
@" " : @"\u00A0",
@"¡" : @"\u00A1",
@"¢" : @"\u00A2",
@"£" : @"\u00A3",
@"¤" : @"\u00A4",
@"¥" : @"\u00A5",
@"¦" : @"\u00A6",
@"§" : @"\u00A7",
@"¨" : @"\u00A8",
@"©" : @"\u00A9",
@"ª" : @"\u00AA",
@"«" : @"\u00AB",
@"¬" : @"\u00AC",
@"­" : @"\u00AD",
@"®" : @"\u00AE",
@"¯" : @"\u00AF",
@"°" : @"\u00B0",
@"±" : @"\u00B1",
@"²" : @"\u00B2",
@"³" : @"\u00B3",
@"´" : @"\u00B4",
@"µ" : @"\u00B5",
@"¶" : @"\u00B6",
@"·" : @"\u00B7",
@"¸" : @"\u00B8",
@"¹" : @"\u00B9",
@"º" : @"\u00BA",
@"»" : @"\u00BB",
@"¼" : @"\u00BC",
@"½" : @"\u00BD",
@"¾" : @"\u00BE",
@"¿" : @"\u00BF",
@"À" : @"\u00C0",
@"Á" : @"\u00C1",
@"Â" : @"\u00C2",
@"Ã" : @"\u00C3",
@"Ä" : @"\u00C4",
@"Å" : @"\u00C5",
@"Æ" : @"\u00C6",
@"Ç" : @"\u00C7",
@"È" : @"\u00C8",
@"É" : @"\u00C9",
@"Ê" : @"\u00CA",
@"Ë" : @"\u00CB",
@"Ì" : @"\u00CC",
@"Í" : @"\u00CD",
@"Î" : @"\u00CE",
@"Ï" : @"\u00CF",
@"Ð" : @"\u00D0",
@"Ñ" : @"\u00D1",
@"Ò" : @"\u00D2",
@"Ó" : @"\u00D3",
@"Ô" : @"\u00D4",
@"Õ" : @"\u00D5",
@"Ö" : @"\u00D6",
@"×" : @"\u00D7",
@"Ø" : @"\u00D8",
@"Ù" : @"\u00D9",
@"Ú" : @"\u00DA",
@"Û" : @"\u00DB",
@"Ü" : @"\u00DC",
@"Ý" : @"\u00DD",
@"Þ" : @"\u00DE",
@"ß" : @"\u00DF",
@"à" : @"\u00E0",
@"á" : @"\u00E1",
@"â" : @"\u00E2",
@"ã" : @"\u00E3",
@"ä" : @"\u00E4",
@"å" : @"\u00E5",
@"æ" : @"\u00E6",
@"ç" : @"\u00E7",
@"è" : @"\u00E8",
@"é" : @"\u00E9",
@"ê" : @"\u00EA",
@"ë" : @"\u00EB",
@"ì" : @"\u00EC",
@"í" : @"\u00ED",
@"î" : @"\u00EE",
@"ï" : @"\u00EF",
@"ð" : @"\u00F0",
@"ñ" : @"\u00F1",
@"ò" : @"\u00F2",
@"ó" : @"\u00F3",
@"ô" : @"\u00F4",
@"õ" : @"\u00F5",
@"ö" : @"\u00F6",
@"÷" : @"\u00F7",
@"ø" : @"\u00F8",
@"ù" : @"\u00F9",
@"ú" : @"\u00FA",
@"û" : @"\u00FB",
@"ü" : @"\u00FC",
@"ý" : @"\u00FD",
@"þ" : @"\u00FE",
@"ÿ" : @"\u00FF",
@"ƒ" : @"\u0192",
@"Α" : @"\u0391",
@"Β" : @"\u0392",
@"Γ" : @"\u0393",
@"Δ" : @"\u0394",
@"Ε" : @"\u0395",
@"Ζ" : @"\u0396",
@"Η" : @"\u0397",
@"Θ" : @"\u0398",
@"Ι" : @"\u0399",
@"Κ" : @"\u039A",
@"Λ" : @"\u039B",
@"Μ" : @"\u039C",
@"Ν" : @"\u039D",
@"Ξ" : @"\u039E",
@"Ο" : @"\u039F",
@"Π" : @"\u03A0",
@"Ρ" : @"\u03A1",
@"Σ" : @"\u03A3",
@"Τ" : @"\u03A4",
@"Υ" : @"\u03A5",
@"Φ" : @"\u03A6",
@"Χ" : @"\u03A7",
@"Ψ" : @"\u03A8",
@"Ω" : @"\u03A9",
@"α" : @"\u03B1",
@"β" : @"\u03B2",
@"γ" : @"\u03B3",
@"δ" : @"\u03B4",
@"ε" : @"\u03B5",
@"ζ" : @"\u03B6",
@"η" : @"\u03B7",
@"θ" : @"\u03B8",
@"ι" : @"\u03B9",
@"κ" : @"\u03BA",
@"λ" : @"\u03BB",
@"μ" : @"\u03BC",
@"ν" : @"\u03BD",
@"ξ" : @"\u03BE",
@"ο" : @"\u03BF",
@"π" : @"\u03C0",
@"ρ" : @"\u03C1",
@"ς" : @"\u03C2",
@"σ" : @"\u03C3",
@"τ" : @"\u03C4",
@"υ" : @"\u03C5",
@"φ" : @"\u03C6",
@"χ" : @"\u03C7",
@"ψ" : @"\u03C8",
@"ω" : @"\u03C9",
@"ϑ" : @"\u03D1",
@"ϒ" : @"\u03D2",
@"ϖ" : @"\u03D6",
@"•" : @"\u2022",
@"…" : @"\u2026",
@"′" : @"\u2032",
@"″" : @"\u2033",
@"‾" : @"\u203E",
@"⁄" : @"\u2044",
@"℘" : @"\u2118",
@"ℑ" : @"\u2111",
@"ℜ" : @"\u211C",
@"™" : @"\u2122",
@"ℵ" : @"\u2135",
@"←" : @"\u2190",
@"↑" : @"\u2191",
@"→" : @"\u2192",
@"↓" : @"\u2193",
@"↔" : @"\u2194",
@"↵" : @"\u21B5",
@"⇐" : @"\u21D0",
@"⇑" : @"\u21D1",
@"⇒" : @"\u21D2",
@"⇓" : @"\u21D3",
@"⇔" : @"\u21D4",
@"∀" : @"\u2200",
@"∂" : @"\u2202",
@"∃" : @"\u2203",
@"∅" : @"\u2205",
@"∇" : @"\u2207",
@"∈" : @"\u2208",
@"∉" : @"\u2209",
@"∋" : @"\u220B",
@"∏" : @"\u220F",
@"∑" : @"\u2211",
@"−" : @"\u2212",
@"∗" : @"\u2217",
@"√" : @"\u221A",
@"∝" : @"\u221D",
@"∞" : @"\u221E",
@"∠" : @"\u2220",
@"∧" : @"\u2227",
@"∨" : @"\u2228",
@"∩" : @"\u2229",
@"∪" : @"\u222A",
@"∫" : @"\u222B",
@"∴" : @"\u2234",
@"∼" : @"\u223C",
@"≅" : @"\u2245",
@"≈" : @"\u2248",
@"≠" : @"\u2260",
@"≡" : @"\u2261",
@"≤" : @"\u2264",
@"≥" : @"\u2265",
@"⊂" : @"\u2282",
@"⊃" : @"\u2283",
@"⊄" : @"\u2284",
@"⊆" : @"\u2286",
@"⊇" : @"\u2287",
@"⊕" : @"\u2295",
@"⊗" : @"\u2297",
@"⊥" : @"\u22A5",
@"⋅" : @"\u22C5",
@"⌈" : @"\u2308",
@"⌉" : @"\u2309",
@"⌊" : @"\u230A",
@"⌋" : @"\u230B",
@"〈" : @"\u2329",
@"〉" : @"\u232A",
@"◊" : @"\u25CA",
@"♠" : @"\u2660",
@"♣" : @"\u2663",
@"♥" : @"\u2665",
@"♦" : @"\u2666"
};
});
}
@end
================================================
FILE: Classes/GONMarkupParser_All.h
================================================
//
// GONMarkupParser_All.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 15/07/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParser.h"
#import "GONMarkupParserManager.h"
#import "GONMarkupDefaultMarkups.h"
#import "GONMarkupParser_Categories.h"
================================================
FILE: Classes/GONMarkupParser_Categories.h
================================================
//
// GONMarkupParser_Categories.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#ifndef GONMarkupParserSample_GONMarkupParser_Categories_h
#define GONMarkupParserSample_GONMarkupParser_Categories_h
#import "UIButton+GONMarkupParser.h"
#import "UILabel+GONMarkupParser.h"
#import "UITextField+GONMarkupParser.h"
#import "UITextView+GONMarkupParser.h"
#endif
================================================
FILE: Classes/GONMarkupReset.h
================================================
//
// GONMarkupReset.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Reset tag is used to reset all current configuration, and use default one
// It allows you to protect a string portion, for example :
// <red>this text is red<strong>red strong <reset> <yellow> yellow default <yellow> </reset> red strong </strong> red </red>
//
// "all" attribute can be used to also remove default configuration
// sharedContext will also be reset
#import "GONMarkup.h"
// Context
#define GONMarkupReset_SAVED_CONTEXT_KEY @"GONMarkupReset.savedContext" // Saved context before reset, to be restored on tag end
// Tag
#define GONMarkupReset_TAG @"reset"
// Attributes
#define GONMarkupReset_TAG_all_ATT @"all" // If true default parser configuration won't be set, default is false.
@interface GONMarkupReset : GONMarkup
/* Class constructor */
+ (instancetype)resetMarkup;
@end
================================================
FILE: Classes/GONMarkupReset.m
================================================
//
// GONMarkupReset.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupReset.h"
#import "GONMarkup+Private.h"
@implementation GONMarkupReset
#pragma mark - Constructor
+ (instancetype)resetMarkup
{
return [self markupForTag:GONMarkupReset_TAG];
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Reset configuration
[configurationDictionary removeAllObjects];
// Set it back to default if allowed
if (![[dicAttributes objectForKey:GONMarkupReset_TAG_all_ATT] boolValue])
[configurationDictionary addEntriesFromDictionary:self.parser.defaultConfiguration];
// Copy current context
NSMutableDictionary *contextCopy = [context mutableCopy];
// Reset it
[context removeAllObjects];
// Hold saved context
[context setObject:contextCopy
forKey:GONMarkupReset_SAVED_CONTEXT_KEY];
}
- (void)closingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// Copy current context
NSMutableDictionary *savedContext = [context objectForKey:GONMarkupReset_SAVED_CONTEXT_KEY];
// Reset context
[context removeAllObjects];
// Reset current context to previously saved one
[context addEntriesFromDictionary:savedContext];
}
@end
================================================
FILE: Classes/GONMarkupSimple.h
================================================
//
// GONMarkupSimple.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 18/07/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
// Define a markup to automatically add attributes to current string
// Content of style dictionary is added to current style dictionary
// "style" dictionary is intended to be the same as you may pass to configure an NSMutableAttributedString using -setAttributes:range: method
//
// If overriding NSParagraphStyleAttributeName, check merging strategy to use
// - override (default)
// - merge
//
#import "GONMarkup.h"
typedef NS_ENUM(NSInteger, GONMarkupSimpleMergingStrategy) {
GONMarkupSimpleMergingStrategyMergeAll = -1,
GONMarkupSimpleMergingStrategyOverride = 0,
GONMarkupSimpleMergingStrategyMergeAlignment = 1 << 0,
GONMarkupSimpleMergingStrategyMergeFirstLineHeadIndent = 1 << 1,
GONMarkupSimpleMergingStrategyMergeHeadIndent = 1 << 2,
GONMarkupSimpleMergingStrategyMergeTailIndent = 1 << 3,
GONMarkupSimpleMergingStrategyMergeLineBreakMode = 1 << 4,
GONMarkupSimpleMergingStrategyMergeMaximumLineHeight = 1 << 5,
GONMarkupSimpleMergingStrategyMergeMinimumLineHeight = 1 << 6,
GONMarkupSimpleMergingStrategyMergeLineSpacing = 1 << 7,
GONMarkupSimpleMergingStrategyMergeParagraphSpacing = 1 << 8,
GONMarkupSimpleMergingStrategyMergeParagraphSpacingBefore = 1 << 9,
GONMarkupSimpleMergingStrategyMergeBaseWritingDirection = 1 << 10,
GONMarkupSimpleMergingStrategyMergeLineHeightMultiple = 1 << 11,
GONMarkupSimpleMergingStrategyMergeTabStops = 1 << 12,
GONMarkupSimpleMergingStrategyMergeDefaultTabInterval = 1 << 13,
GONMarkupSimpleMergingStrategyMergeHyphenationFactor = 1 << 14
};
@interface GONMarkupSimple : GONMarkup
/* Class contructor, allowing to specify a merging strategy.
* Use this constructor if this markup will update NSParagraphStyleAttributeName key
* strategy can be a combinaison of GONMarkupSimpleMergingStrategy values to define per attribute merging strategy
* "style" dictionary is intended to be the same as you may pass to configure an NSMutableAttributedString using -setAttributes:range: method
*/
+ (instancetype)simpleMarkup:(NSString *)tag style:(NSDictionary *)style mergingStrategy:(GONMarkupSimpleMergingStrategy)strategy;
/* Default class contructor, using override as default merging strategy
* Use this constructor is this markup won't update NSParagraphStyleAttributeName key, will throw an exception otherwise
* "style" dictionary is intended to be the same as you may pass to configure an NSMutableAttributedString using -setAttributes:range: method
*/
+ (instancetype)simpleMarkup:(NSString *)tag style:(NSDictionary *)style;
@property (nonatomic, copy, readonly) NSDictionary *style;
@end
================================================
FILE: Classes/GONMarkupSimple.m
================================================
//
// GONMarkupSimple.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 18/07/14.
// Copyright 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupSimple.h"
#define MERGE_ATTRIBUTE(att) (_mergingStrategy == GONMarkupSimpleMergingStrategyMergeAll || _mergingStrategy | att)
@interface GONMarkupSimple ()
// Data
@property (nonatomic, copy) NSDictionary *style;
@property (nonatomic, assign) GONMarkupSimpleMergingStrategy mergingStrategy;
@end
@implementation GONMarkupSimple
#pragma mark - Constructor
+ (instancetype)simpleMarkup:(NSString *)tag
style:(NSDictionary *)style
mergingStrategy:(GONMarkupSimpleMergingStrategy)strategy
{
GONMarkupSimple *markup = [self markupForTag:tag];
markup.style = style;
markup.mergingStrategy = strategy;
return markup;
}
+ (instancetype)simpleMarkup:(NSString *)tag
style:(NSDictionary *)style
{
if ([style objectForKey:NSParagraphStyleAttributeName])
@throw @"Critical error. You cannot update NSParagraphStyleAttributeName without setting a merging strategy ";
GONMarkupSimple *markup = [self markupForTag:tag];
markup.style = style;
return markup;
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
// No NSParagraphStyleAttributeName update, or set in only one dic, nothing to do
if ((![_style objectForKey:NSParagraphStyleAttributeName] && ![configurationDictionary objectForKey:NSParagraphStyleAttributeName]) ||
( [_style objectForKey:NSParagraphStyleAttributeName] && ![configurationDictionary objectForKey:NSParagraphStyleAttributeName]) ||
(![_style objectForKey:NSParagraphStyleAttributeName] && [configurationDictionary objectForKey:NSParagraphStyleAttributeName]))
{
[configurationDictionary addEntriesFromDictionary:_style];
return;
}
// Should apply a defined strategy
NSMutableDictionary *updatedStyleDic = [_style mutableCopy];
[updatedStyleDic setObject:[self merge:[_style objectForKey:NSParagraphStyleAttributeName] into:[configurationDictionary objectForKey:NSParagraphStyleAttributeName]]
forKey:NSParagraphStyleAttributeName];
[configurationDictionary addEntriesFromDictionary:_style];
}
- (NSParagraphStyle *)merge:(NSParagraphStyle *)sourceStyle
into:(NSParagraphStyle *)destStyle
{
// On override, nothing to do
if (_mergingStrategy == GONMarkupSimpleMergingStrategyOverride)
return [sourceStyle mutableCopy];
// Merge all strategy
NSMutableParagraphStyle *mergedStyle = [destStyle mutableCopy];
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeAlignment)) mergedStyle.alignment = sourceStyle.alignment;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeFirstLineHeadIndent)) mergedStyle.firstLineHeadIndent = sourceStyle.firstLineHeadIndent;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeHeadIndent)) mergedStyle.headIndent = sourceStyle.headIndent;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeTailIndent)) mergedStyle.tailIndent = sourceStyle.tailIndent;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeLineBreakMode)) mergedStyle.lineBreakMode = sourceStyle.lineBreakMode;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeMaximumLineHeight)) mergedStyle.maximumLineHeight = sourceStyle.maximumLineHeight;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeMinimumLineHeight)) mergedStyle.minimumLineHeight = sourceStyle.minimumLineHeight;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeLineSpacing)) mergedStyle.lineSpacing = sourceStyle.lineSpacing;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeParagraphSpacing)) mergedStyle.paragraphSpacing = sourceStyle.paragraphSpacing;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeParagraphSpacingBefore)) mergedStyle.paragraphSpacingBefore = sourceStyle.paragraphSpacingBefore;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeBaseWritingDirection)) mergedStyle.baseWritingDirection = sourceStyle.baseWritingDirection;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeLineHeightMultiple)) mergedStyle.lineHeightMultiple = sourceStyle.lineHeightMultiple;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeTabStops)) mergedStyle.tabStops = sourceStyle.tabStops;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeDefaultTabInterval)) mergedStyle.defaultTabInterval = sourceStyle.defaultTabInterval;
if (MERGE_ATTRIBUTE(GONMarkupSimpleMergingStrategyMergeHyphenationFactor)) mergedStyle.hyphenationFactor = sourceStyle.hyphenationFactor;
return mergedStyle;
}
@end
================================================
FILE: Classes/GONMarkupStrong.h
================================================
//
// GONMarkupStrong.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/05/2017.
// Copyright 2017 Nicolas Goutaland. All rights reserved.
//
// Same as b tag.
// Subclassing b allowing users to override strong markup behavior
//
// Examples
//
// <strong>bold text</>
#import "GONMarkupBold.h"
#define GONMarkupStrong_TAG @"strong"
@interface GONMarkupStrong : GONMarkupBold
/* Default markup to add strong support */
+ (instancetype)strongMarkup;
@end
================================================
FILE: Classes/GONMarkupStrong.m
================================================
//
// GONMarkupStrong.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/05/2017.
// Copyright 2017 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupStrong.h"
@interface GONMarkupStrong ()
@end
@implementation GONMarkupStrong
#pragma mark - Constructor
+ (instancetype)strongMarkup
{
return [super fontTraitsMarkup:GONMarkupStrong_TAG
traits:UIFontDescriptorTraitBold];
}
@end
================================================
FILE: Classes/GONMarkupTextStyle.h
================================================
//
// GONMarkupTextStyle.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/07/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
// Tag updating current font style (subscripted / superscripted).
//
// Examples
//
// <sup>superscripted text</>
// <sub>subscripted text</>
#import "GONMarkup.h"
// Tag
#define GONMarkupTextStyle_Superscripted_TAG @"sup"
#define GONMarkupTextStyle_Subscripted_TAG @"sub"
@interface GONMarkupTextStyle : GONMarkup
/* Return all markups */
+ (NSArray *)allMarkups;
/* Default markup to add superscripted support */
+ (instancetype)superscriptedMarkup;
/* Default markup to add subscripted support */
+ (instancetype)subscriptedMarkup;
@end
================================================
FILE: Classes/GONMarkupTextStyle.m
================================================
//
// GONMarkupTextStyle.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/07/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupTextStyle.h"
#import <CoreText/CoreText.h>
@interface GONMarkupTextStyle ()
// Data
@property (nonatomic, strong) NSNumber *superscriptValue;
@end
@implementation GONMarkupTextStyle
#pragma mark - Constructor
+ (NSArray *)allMarkups
{
return @[[self superscriptedMarkup], [self subscriptedMarkup]];
}
+ (instancetype)superscriptedMarkup
{
GONMarkupTextStyle *markup = [self markupForTag:GONMarkupTextStyle_Superscripted_TAG];
markup.superscriptValue = @(1);
return markup;
}
+ (instancetype)subscriptedMarkup
{
GONMarkupTextStyle *markup = [self markupForTag:GONMarkupTextStyle_Subscripted_TAG];
markup.superscriptValue = @(-1);
return markup;
}
#pragma mark - Style
- (void)openingMarkupFound:(NSString *)tag
configuration:(NSMutableDictionary *)configurationDictionary
context:(NSMutableDictionary *)context
attributes:(NSDictionary *)dicAttributes
resultString:(NSAttributedString *)resultString
{
[configurationDictionary setObject:_superscriptValue
forKey:(NSString *)kCTSuperscriptAttributeName];
}
@end
================================================
FILE: Classes/UIButton+GONMarkupParser.h
================================================
//
// UIButton+GONMarkupParser.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 23/08/15.
// Copyright (c) 2015 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParser.h"
@interface UIButton (GONMarkupParser)
/* Update button label with given text, considering it as a marked up string.
* Given parser will be used to parse string
* If parser is nil, [GONMarkupParserManager sharedParser] will be used
*/
- (void)setMarkedUpTitle:(NSString *)title forState:(UIControlState)controlState parser:(GONMarkupParser *)parser;
/* Update button with given text, considering it as a marked up string.
* [GONMarkupParserManager sharedParser] will be used to parse string
*/
- (void)setMarkedUpTitle:(NSString *)title forState:(UIControlState)controlState;
@end
================================================
FILE: Classes/UIButton+GONMarkupParser.m
================================================
//
// UIButton+GONMarkupParser.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 23/08/15.
// Copyright (c) 2015 Nicolas Goutaland. All rights reserved.
//
#import "UIButton+GONMarkupParser.h"
#import "GONMarkupParserUtils.h"
@implementation UIButton (GONMarkupParser)
#pragma mark - GONMarkupParser
- (void)setMarkedUpTitle:(NSString *)title forState:(UIControlState)controlState parser:(GONMarkupParser *)parser
{
NSAttributedString *attributedText = [GONMarkupParserUtils attributedString:parser
withString:title
defautColor:[self titleColorForState:controlState]
defaultFont:self.titleLabel.font
defaultAlignment:self.titleLabel.textAlignment];
[self setAttributedTitle:attributedText
forState:controlState];
}
- (void)setMarkedUpTitle:(NSString *)title forState:(UIControlState)controlState
{
[self setMarkedUpTitle:title
forState:UIControlStateNormal
parser:nil];
}
@end
================================================
FILE: Classes/UILabel+GONMarkupParser.h
================================================
//
// UILabel+GONMarkupParser.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParser.h"
@interface UILabel (GONMarkupParser)
/* Update label with given text, considering it as a marked up string.
* Given parser will be used to parse string
* If parser is nil, [GONMarkupParserManager sharedParser] will be used
*/
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser;
/* Update label with given text, considering it as a marked up string.
* [GONMarkupParserManager sharedParser] will be used to parse string
*/
- (void)setMarkedUpText:(NSString *)text;
@end
================================================
FILE: Classes/UILabel+GONMarkupParser.m
================================================
//
// UILabel+GONMarkupParser.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "UILabel+GONMarkupParser.h"
#import "GONMarkupParserManager.h"
#import "GONMarkupParserUtils.h"
@implementation UILabel (GONMarkupParser)
#pragma mark - GONMarkupParser
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser
{
self.attributedText = [GONMarkupParserUtils attributedString:parser
withString:text
defautColor:self.textColor
defaultFont:self.font
defaultAlignment:self.textAlignment];
}
- (void)setMarkedUpText:(NSString *)text
{
[self setMarkedUpText:text
parser:nil];
}
@end
================================================
FILE: Classes/UITextField+GONMarkupParser.h
================================================
//
// UITextField+GONMarkupParser.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParser.h"
@interface UITextField (GONMarkupParser)
/* Update textfield with given text, considering it as a marked up string.
* Given parser will be used to parse string
* If parser is nil, [GONMarkupParserManager sharedParser] will be used
*/
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser;
/* Update textfield with given text, considering it as a marked up string.
* [GONMarkupParserManager sharedParser] will be used to parse string
*/
- (void)setMarkedUpText:(NSString *)text;
@end
================================================
FILE: Classes/UITextField+GONMarkupParser.m
================================================
//
// UITextField+GONMarkupParser.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 08/09/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "UITextField+GONMarkupParser.h"
#import "GONMarkupParserManager.h"
#import "GONMarkupParserUtils.h"
@implementation UITextField (GONMarkupParser)
#pragma mark - GONMarkupParser
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser
{
self.attributedText = [GONMarkupParserUtils attributedString:parser
withString:text
defautColor:self.textColor
defaultFont:self.font
defaultAlignment:self.textAlignment];
}
- (void)setMarkedUpText:(NSString *)text
{
[self setMarkedUpText:text
parser:nil];
}
@end
================================================
FILE: Classes/UITextView+GONMarkupParser.h
================================================
//
// UITextView+GONMarkupParser.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
#import "GONMarkupParser.h"
@interface UITextView (GONMarkupParser)
/* Update textfield with given text, considering it as a marked up string.
* Given parser will be used to parse string
* If parser is nil, [GONMarkupParserManager sharedParser] will be used
*/
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser;
/* Update textfield with given text, considering it as a marked up string.
* [GONMarkupParserManager sharedParser] will be used to parse string
*/
- (void)setMarkedUpText:(NSString *)text;
@end
================================================
FILE: Classes/UITextView+GONMarkupParser.m
================================================
//
// UITextView+GONMarkupParser.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 04/02/15.
// Copyright 2015 Nicolas Goutaland. All rights reserved.
//
#import "UITextView+GONMarkupParser.h"
#import "GONMarkupParserManager.h"
#import "GONMarkupParserUtils.h"
@implementation UITextView (GONMarkupParser)
#pragma mark - GONMarkupParser
- (void)setMarkedUpText:(NSString *)text parser:(GONMarkupParser *)parser
{
self.attributedText = [GONMarkupParserUtils attributedString:parser
withString:text
defautColor:self.textColor
defaultFont:self.font
defaultAlignment:self.textAlignment];
}
- (void)setMarkedUpText:(NSString *)text
{
[self setMarkedUpText:text
parser:nil];
}
@end
================================================
FILE: Example/GONMarkupParserSample/AppDelegate.h
================================================
//
// AppDelegate.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) IBOutlet UIWindow *window;
@end
================================================
FILE: Example/GONMarkupParserSample/AppDelegate.m
================================================
//
// AppDelegate.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 25/06/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "AppDelegate.h"
#import "GONMarkupParser_All.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Set your custom configuration here
#ifdef DEBUG
[GONMarkupParserManager sharedParser].logLevel = GONMarkupParserLogLevelErrors; // Fuck yeah, error logging
#endif
return YES;
}
@end
================================================
FILE: Example/GONMarkupParserSample/ComplexExample
================================================
default text here <red>this text should be red <small>small </small> </red><small>black small</small>.
This one should be black
<font name="Didot-Italic" size="12"> Using a new font, using its full name (Didot-Italic) </><br/><br/>
<color value="F4D03F">yellow <reset>protected \o/</reset>ye<font size="12">ll</>ow<//>
Let me add some <custom>Custom tag here</>
Yep, that was big.
You can also change text alignment
<center>Centered</>
<left>Left</>
<right>Right</>
<justified>Justified paragraph Lorem ipsum dolor sit amet, consectetur adipiscing elit. In <red>rhoncus diam nec sem consequat</red> tincidunt. Etiam eu lacus a lorem cursus sodales et nec sapien. </>
<natural>Natural</>
<inc>Text <inc>size <inc>can <inc>also <inc>be <inc>incremented <dec>or <dec>decremented<dec>, <dec>1 <dec>per <dec>1, <inc value="15">or specifying <dec value="10">a value.<//>
<font name="customfont">This text will be written using a registered font on your parser, allowing to use it specifying a code, instead of its full name.</>
<custom>Another Custom tag here, but with <reset all="true">a full protected section</reset>, pretty cool huh ?</>
And now, an unordered list
<ul>
<li>Item</li>
<ul>
<li>very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Item</li>
<ul>
<li>very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Item</li>
<li>very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Item<br />with a new line inside.....</li>
</ul>
<ul>
<li>Item</li>
</ul>
</ul>
<li><red>Item</red></li>
<li>Item</li>
</ul>
and an ordered list
<ol>
<li>Item 1</li>
<ol>
<li>Item 1</li>
<ol>
<li>Item 1</li>
</ol>
<ol>
<li>Item 1 (New sub list)</li>
</ol>
</ol>
<li>Item 2</li>
<li>Item 3</li></ol>
<dec><dec><color value="gray">Anchor are also supported. If you are using a UITextView to display your attributed string, set it non editable, but selectionnable, set its delegate and implements
<b>- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange</>
method to handle user interaction.
<a href="https://github.com/nicolasgoutaland/GONMarkupParser">Link example ;)<//>
Finally, you can set <b>bold text</>, <i>italic text</> or <b><i>both</></>
<u>Underline</> and <strike>strikethrough</> text is also available.
You can customize line <u pattern="dash">pattern</>, <strike style="double">style</> and <u word="true">limit style to words only</>.
<strike pattern="dashdotdot" words="true"><u style="double">Ugly style is also available ;)</></>
================================================
FILE: Example/GONMarkupParserSample/ComplexExampleViewController.h
================================================
//
// ComplexExampleViewController.h
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 11/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
@interface ComplexExampleViewController : UIViewController
@end
================================================
FILE: Example/GONMarkupParserSample/ComplexExampleViewController.m
================================================
//
// ComplexExampleViewController.m
// GONMarkupParserSample
//
// Created by Nicolas Goutaland on 11/11/14.
// Copyright (c) 2014 Nicolas Goutaland. All rights reserved.
//
#import "ComplexExampleViewController.h"
#import "ResultViewController.h"
#define COMPLEX_EXAMPLE_INPUT_FILE @"ComplexExample"
@interface ComplexExampleViewController ()
// Data
@property (nonatomic, strong) GONMarkupParser *parser;
// View controllers
@property (nonatomic, strong) ResultViewController *resultViewController;
// Outlets
@property (nonatomic, weak ) IBOutlet UITextView *inputTextView;
@property (nonatomic, strong) IBOutlet UIBarButtonItem *validateButton;
@end
@implementation ComplexExampleViewController
#pragma mark - View management
- (void)viewDidLoad {
[super viewDidLoad];
// Validate button
self.navigationItem.rightBarButtonItem = _validateButton;
// Load default text for a complex example
NSString *defaultString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:COMPLEX_EXAMPLE_INPUT_FILE ofType:nil]
encoding:NSUTF8StringEncoding error:nil];
self.inputTextView.text = defaultString;
[self configureParser];
}
#pragma mark - Parser configuration
- (void)configureParser
{
// Create a new parser
_parser = [GONMarkupParser defaultMarkupParser];
// Set your custom configuration here
#ifdef DEBUG
_parser.logLevel = GONMarkupParserLogLevelErrors; // Fuck yeah, error logging
#endif
// Set default text configuration
[_parser.defaultConfiguration setObject:[UIFont systemFontOfSize:25.0]
forKey:NSFontAttributeName];
// Add a custom markup, that will center text when used, and display it in pink.
NSMutableParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
defaultParagraphStyle.alignment = NSTextAlignmentCenter;
[_parser addMarkup:[GONMarkupSimple simpleMarkup:@"pwet"
style:@{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSForegroundColorAttributeName : [@"pink" representedColor] // NSString+Color
}
mergingStrategy:GONMarkupSimpleMergingStrategyMergeAll]];
// Add add font markup, to display small text when encountered
[_parser addMarkup:[GONMarkupNamedFont namedFontMarkup:[UIFont systemFontOfSize:12.0] forTag:@"small"]];
// Add a convenient tag for red color
[_parser addMarkup:[GONMarkupNamedColor namedColorMarkup:[UIColor redColor]
forTag:@"red"]];
// Custom markup, based on block
GONMarkupBlock *markupBlock = [GONMarkupBlock blockMarkup:@"custom"];
markupBlock.openingMarkupBlock = ^(NSMutableDictionary *configurationDictionary, NSString *tag, NSMutableDictionary *context, NSDictionary *dicAttributes, NSAttributedString *resultString) {
[configurationDictionary setObject:[UIFont boldSystemFontOfSize:69.0]
forKey:NSFontAttributeName];
[configurationDictionary setObject:[@"brown" representedColor]
forKey:NSForegroundColorAttributeName];
};
[_parser addMarkup:markupBlock];
// Finally registering a custom font. You can refer to it using its key
[_parser registerFont:[UIFont fontWithName:@"AmericanTypewriter" size:22]
forKey:@"customfont"];
}
#pragma mark - UI Actions
- (IBAction)showResult
{
// Parse string
self.resultViewController.resultAttributedString = [_parser attributedStringFromString:_inputTextView.text];
// Show results
[self.navigationController pushViewController:self.resultViewController
animated:YES];
}
LAZY_PROPERTY(resultViewController);
@end
================================================
FILE: Example/GONMarkupParserSample/ComplexExampleViewController.xib
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ComplexExampleViewController">
<connections>
<outlet property="inputTextView" destination="POq-EJ-nFs" id="tde-IK-RHB"/>
<outlet property="validateButton" destination="eLD-U2-qOP" id="dM4-Fu-LKp"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
gitextract_dlvgp0vj/ ├── .gitignore ├── CHANGELOG.md ├── Classes/ │ ├── GONMarkup+Private.h │ ├── GONMarkup+Private.m │ ├── GONMarkup.h │ ├── GONMarkup.m │ ├── GONMarkupAlignment.h │ ├── GONMarkupAlignment.m │ ├── GONMarkupAnchor.h │ ├── GONMarkupAnchor.m │ ├── GONMarkupBlock.h │ ├── GONMarkupBlock.m │ ├── GONMarkupBold.h │ ├── GONMarkupBold.m │ ├── GONMarkupColor.h │ ├── GONMarkupColor.m │ ├── GONMarkupDec.h │ ├── GONMarkupDec.m │ ├── GONMarkupDefaultMarkups.h │ ├── GONMarkupFont.h │ ├── GONMarkupFont.m │ ├── GONMarkupFontTraits.h │ ├── GONMarkupFontTraits.m │ ├── GONMarkupImage.h │ ├── GONMarkupImage.m │ ├── GONMarkupInc.h │ ├── GONMarkupInc.m │ ├── GONMarkupItalic.h │ ├── GONMarkupItalic.m │ ├── GONMarkupLineBreak.h │ ├── GONMarkupLineBreak.m │ ├── GONMarkupLineStyle.h │ ├── GONMarkupLineStyle.m │ ├── GONMarkupList.h │ ├── GONMarkupList.m │ ├── GONMarkupListItem.h │ ├── GONMarkupListItem.m │ ├── GONMarkupNamedColor.h │ ├── GONMarkupNamedColor.m │ ├── GONMarkupNamedFont.h │ ├── GONMarkupNamedFont.m │ ├── GONMarkupParagraph.h │ ├── GONMarkupParagraph.m │ ├── GONMarkupParser.h │ ├── GONMarkupParser.m │ ├── GONMarkupParserManager.h │ ├── GONMarkupParserManager.m │ ├── GONMarkupParserUtils.h │ ├── GONMarkupParserUtils.m │ ├── GONMarkupParser_All.h │ ├── GONMarkupParser_Categories.h │ ├── GONMarkupReset.h │ ├── GONMarkupReset.m │ ├── GONMarkupSimple.h │ ├── GONMarkupSimple.m │ ├── GONMarkupStrong.h │ ├── GONMarkupStrong.m │ ├── GONMarkupTextStyle.h │ ├── GONMarkupTextStyle.m │ ├── UIButton+GONMarkupParser.h │ ├── UIButton+GONMarkupParser.m │ ├── UILabel+GONMarkupParser.h │ ├── UILabel+GONMarkupParser.m │ ├── UITextField+GONMarkupParser.h │ ├── UITextField+GONMarkupParser.m │ ├── UITextView+GONMarkupParser.h │ └── UITextView+GONMarkupParser.m ├── Example/ │ ├── GONMarkupParserSample/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── ComplexExample │ │ ├── ComplexExampleViewController.h │ │ ├── ComplexExampleViewController.m │ │ ├── ComplexExampleViewController.xib │ │ ├── DefaultConfigurationViewController.h │ │ ├── DefaultConfigurationViewController.m │ │ ├── DefaultConfigurationViewController.xib │ │ ├── GONMarkupParserSample-Info.plist │ │ ├── GONMarkupParserSample-Prefix.pch │ │ ├── Images.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage/ │ │ │ └── Contents.json │ │ ├── MainWindow.xib │ │ ├── PlaygroundViewController.h │ │ ├── PlaygroundViewController.m │ │ ├── PlaygroundViewController.xib │ │ ├── ResultViewController.h │ │ ├── ResultViewController.m │ │ ├── ResultViewController.xib │ │ ├── RootViewController.h │ │ ├── RootViewController.m │ │ ├── RootViewController.xib │ │ ├── SampleViewController.h │ │ ├── SampleViewController.m │ │ ├── SampleViewController.xib │ │ ├── en.lproj/ │ │ │ └── InfoPlist.strings │ │ ├── main.m │ │ └── samples.plist │ ├── GONMarkupParserSample.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ ├── GONMarkupParserSample.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ ├── GONMarkupParserSampleTests/ │ │ ├── GONMarkupParserSampleTests-Info.plist │ │ ├── GONMarkupParserSampleTests.m │ │ └── en.lproj/ │ │ └── InfoPlist.strings │ └── PodFile ├── GONMarkupParser.podspec ├── LICENSE └── README.md
SYMBOL INDEX (2 symbols across 2 files)
FILE: Classes/GONMarkupFontTraits.h
function interface (line 17) | interface GONMarkupFontTraits : GONMarkup {
FILE: Classes/GONMarkupParser.h
type GONMarkupParserLogLevel (line 46) | typedef enum : NSUInteger {
Condensed preview — 107 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (355K chars).
[
{
"path": ".gitignore",
"chars": 325,
"preview": "# Xcode\n.DS_Store\n*/build/*\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspecti"
},
{
"path": "CHANGELOG.md",
"chars": 3101,
"preview": "# Change Log\n## [__0.8.2__](https://github.com/nicolasgoutaland/GONMarkupParser/releases/tag/0.8.2)\nUpdated HTML entitie"
},
{
"path": "Classes/GONMarkup+Private.h",
"chars": 1044,
"preview": "//\n// GONMarkup+Private.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) "
},
{
"path": "Classes/GONMarkup+Private.m",
"chars": 1566,
"preview": "//\n// GONMarkup+Private.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) "
},
{
"path": "Classes/GONMarkup.h",
"chars": 5578,
"preview": "//\n// GONMarkup.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014 Nic"
},
{
"path": "Classes/GONMarkup.m",
"chars": 5874,
"preview": "//\n// GONMarkup.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014 Nic"
},
{
"path": "Classes/GONMarkupAlignment.h",
"chars": 1478,
"preview": "//\n// GONMarkupAlignment.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 22/07/14.\n// Copyright 201"
},
{
"path": "Classes/GONMarkupAlignment.m",
"chars": 2141,
"preview": "//\n// GONMarkupAlignment.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 22/07/14.\n// Copyright 201"
},
{
"path": "Classes/GONMarkupAnchor.h",
"chars": 916,
"preview": "//\n// GONMarkupAnchor.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/02/15.\n// Copyright 2015 N"
},
{
"path": "Classes/GONMarkupAnchor.m",
"chars": 957,
"preview": "//\n// GONMarkupAnchor.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/02/15.\n// Copyright 2015 N"
},
{
"path": "Classes/GONMarkupBlock.h",
"chars": 1658,
"preview": "//\n// GONMarkupBlock.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupBlock.m",
"chars": 3358,
"preview": "//\n// GONMarkupBlock.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupBold.h",
"chars": 789,
"preview": "//\n// GONMarkupBold.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/09/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupBold.m",
"chars": 401,
"preview": "//\n// GONMarkupBold.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/09/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupColor.h",
"chars": 840,
"preview": "//\n// GONMarkupColor.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupColor.m",
"chars": 954,
"preview": "//\n// GONMarkupColor.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupDec.h",
"chars": 651,
"preview": "//\n// GONMarkupDec.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c) 2014 "
},
{
"path": "Classes/GONMarkupDec.m",
"chars": 1328,
"preview": "//\n// GONMarkupDec.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c) 2014 "
},
{
"path": "Classes/GONMarkupDefaultMarkups.h",
"chars": 780,
"preview": "//\n// GONMarkupDefaultMarkups.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyrigh"
},
{
"path": "Classes/GONMarkupFont.h",
"chars": 1964,
"preview": "//\n// GONMarkupFont.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupFont.m",
"chars": 3518,
"preview": "//\n// GONMarkupFont.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupFontTraits.h",
"chars": 1105,
"preview": "//\n// GONMarkupFontTraits.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 22/09/14.\n// Copyright 20"
},
{
"path": "Classes/GONMarkupFontTraits.m",
"chars": 3473,
"preview": "//\n// GONMarkupFontTraits.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 22/09/14.\n// Copyright 20"
},
{
"path": "Classes/GONMarkupImage.h",
"chars": 644,
"preview": "//\n// GONMarkupImage.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/03/16.\n// Copyright © 2016 "
},
{
"path": "Classes/GONMarkupImage.m",
"chars": 1396,
"preview": "//\n// GONMarkupImage.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/03/16.\n// Copyright © 2016 "
},
{
"path": "Classes/GONMarkupInc.h",
"chars": 649,
"preview": "//\n// GONMarkupInc.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c) 2014 "
},
{
"path": "Classes/GONMarkupInc.m",
"chars": 1320,
"preview": "//\n// GONMarkupInc.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c) 2014 "
},
{
"path": "Classes/GONMarkupItalic.h",
"chars": 817,
"preview": "//\n// GONMarkupItalic.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/09/14.\n// Copyright (c) 20"
},
{
"path": "Classes/GONMarkupItalic.m",
"chars": 443,
"preview": "//\n// GONMarkupItalic.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/09/14.\n// Copyright (c) 20"
},
{
"path": "Classes/GONMarkupLineBreak.h",
"chars": 448,
"preview": "//\n// GONMarkupLineBreak.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupLineBreak.m",
"chars": 1166,
"preview": "//\n// GONMarkupLineBreak.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupLineStyle.h",
"chars": 1950,
"preview": "//\n// GONMarkupLineStyle.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 03/02/15.\n// Copyright 201"
},
{
"path": "Classes/GONMarkupLineStyle.m",
"chars": 3495,
"preview": "//\n// GONMarkupLineStyle.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 03/02/15.\n// Copyright 201"
},
{
"path": "Classes/GONMarkupList.h",
"chars": 1180,
"preview": "//\n// GONMarkupList.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupList.m",
"chars": 2887,
"preview": "//\n// GONMarkupList.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014"
},
{
"path": "Classes/GONMarkupListItem.h",
"chars": 1772,
"preview": "//\n// GONMarkupListItem.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) "
},
{
"path": "Classes/GONMarkupListItem.m",
"chars": 6741,
"preview": "//\n// GONMarkupListItem.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) "
},
{
"path": "Classes/GONMarkupNamedColor.h",
"chars": 591,
"preview": "//\n// GONMarkupNamedColor.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c"
},
{
"path": "Classes/GONMarkupNamedColor.m",
"chars": 1515,
"preview": "//\n// GONMarkupNamedColor.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c"
},
{
"path": "Classes/GONMarkupNamedFont.h",
"chars": 591,
"preview": "//\n// GONMarkupNamedFont.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupNamedFont.m",
"chars": 1454,
"preview": "//\n// GONMarkupNamedFont.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupParagraph.h",
"chars": 500,
"preview": "//\n// GONMarkupParagraph.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 06/08/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupParagraph.m",
"chars": 1575,
"preview": "//\n// GONMarkupParagraph.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 06/08/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupParser.h",
"chars": 6476,
"preview": "//\n// GONMarkupParser.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 20"
},
{
"path": "Classes/GONMarkupParser.m",
"chars": 24888,
"preview": "//\n// GONMarkupParser.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 20"
},
{
"path": "Classes/GONMarkupParserManager.h",
"chars": 942,
"preview": "//\n// GONMarkupParserManager.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/07/14.\n// Copyright"
},
{
"path": "Classes/GONMarkupParserManager.m",
"chars": 1561,
"preview": "//\n// GONMarkupParserManager.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/07/14.\n// Copyright"
},
{
"path": "Classes/GONMarkupParserUtils.h",
"chars": 1435,
"preview": "//\n// GONMarkupParserUtils.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/08/14.\n// Copyright ("
},
{
"path": "Classes/GONMarkupParserUtils.m",
"chars": 48798,
"preview": "//\n// GONMarkupParserUtils.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/08/14.\n// Copyright ("
},
{
"path": "Classes/GONMarkupParser_All.h",
"chars": 307,
"preview": "//\n// GONMarkupParser_All.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 15/07/14.\n// Copyright (c"
},
{
"path": "Classes/GONMarkupParser_Categories.h",
"chars": 457,
"preview": "//\n// GONMarkupParser_Categories.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/09/14.\n// Copyr"
},
{
"path": "Classes/GONMarkupReset.h",
"chars": 1003,
"preview": "//\n// GONMarkupReset.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupReset.m",
"chars": 1824,
"preview": "//\n// GONMarkupReset.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 201"
},
{
"path": "Classes/GONMarkupSimple.h",
"chars": 2965,
"preview": "//\n// GONMarkupSimple.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 18/07/14.\n// Copyright 2014 N"
},
{
"path": "Classes/GONMarkupSimple.m",
"chars": 5211,
"preview": "//\n// GONMarkupSimple.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 18/07/14.\n// Copyright 2014 N"
},
{
"path": "Classes/GONMarkupStrong.h",
"chars": 500,
"preview": "//\n// GONMarkupStrong.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/05/2017.\n// Copyright 2017"
},
{
"path": "Classes/GONMarkupStrong.m",
"chars": 446,
"preview": "//\n// GONMarkupStrong.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/05/2017.\n// Copyright 2017"
},
{
"path": "Classes/GONMarkupTextStyle.h",
"chars": 736,
"preview": "//\n// GONMarkupTextStyle.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/07/14.\n// Copyright (c)"
},
{
"path": "Classes/GONMarkupTextStyle.m",
"chars": 1335,
"preview": "//\n// GONMarkupTextStyle.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/07/14.\n// Copyright (c)"
},
{
"path": "Classes/UIButton+GONMarkupParser.h",
"chars": 790,
"preview": "//\n// UIButton+GONMarkupParser.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 23/08/15.\n// Copyrig"
},
{
"path": "Classes/UIButton+GONMarkupParser.m",
"chars": 1243,
"preview": "//\n// UIButton+GONMarkupParser.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 23/08/15.\n// Copyrig"
},
{
"path": "Classes/UILabel+GONMarkupParser.h",
"chars": 700,
"preview": "//\n// UILabel+GONMarkupParser.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/09/14.\n// Copyrigh"
},
{
"path": "Classes/UILabel+GONMarkupParser.m",
"chars": 928,
"preview": "//\n// UILabel+GONMarkupParser.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/09/14.\n// Copyrigh"
},
{
"path": "Classes/UITextField+GONMarkupParser.h",
"chars": 717,
"preview": "//\n// UITextField+GONMarkupParser.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/09/14.\n// Copy"
},
{
"path": "Classes/UITextField+GONMarkupParser.m",
"chars": 940,
"preview": "//\n// UITextField+GONMarkupParser.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 08/09/14.\n// Copy"
},
{
"path": "Classes/UITextView+GONMarkupParser.h",
"chars": 711,
"preview": "//\n// UITextView+GONMarkupParser.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/02/15.\n// Copyr"
},
{
"path": "Classes/UITextView+GONMarkupParser.m",
"chars": 932,
"preview": "//\n// UITextView+GONMarkupParser.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 04/02/15.\n// Copyr"
},
{
"path": "Example/GONMarkupParserSample/AppDelegate.h",
"chars": 287,
"preview": "//\n// AppDelegate.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014 N"
},
{
"path": "Example/GONMarkupParserSample/AppDelegate.m",
"chars": 563,
"preview": "//\n// AppDelegate.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014 N"
},
{
"path": "Example/GONMarkupParserSample/ComplexExample",
"chars": 2815,
"preview": "default text here <red>this text should be red <small>small </small> </red><small>black small</small>.\nThis one should b"
},
{
"path": "Example/GONMarkupParserSample/ComplexExampleViewController.h",
"chars": 245,
"preview": "//\n// ComplexExampleViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 11/11/14.\n// Cop"
},
{
"path": "Example/GONMarkupParserSample/ComplexExampleViewController.m",
"chars": 4083,
"preview": "//\n// ComplexExampleViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 11/11/14.\n// Cop"
},
{
"path": "Example/GONMarkupParserSample/ComplexExampleViewController.xib",
"chars": 2907,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/DefaultConfigurationViewController.h",
"chars": 256,
"preview": "//\n// DefaultConfigurationViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/11/14.\n/"
},
{
"path": "Example/GONMarkupParserSample/DefaultConfigurationViewController.m",
"chars": 1336,
"preview": "//\n// DefaultConfigurationViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 19/11/14.\n/"
},
{
"path": "Example/GONMarkupParserSample/DefaultConfigurationViewController.xib",
"chars": 5018,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/GONMarkupParserSample-Info.plist",
"chars": 1206,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Example/GONMarkupParserSample/GONMarkupParserSample-Prefix.pch",
"chars": 474,
"preview": "//\n// Prefix header\n//\n// The contents of this file are implicitly included at the beginning of every source file.\n//\n"
},
{
"path": "Example/GONMarkupParserSample/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 849,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n \"size\" : \"20x20\"\n },\n {\n \"idiom\""
},
{
"path": "Example/GONMarkupParserSample/Images.xcassets/LaunchImage.launchimage/Contents.json",
"chars": 516,
"preview": "{\n \"images\" : [\n {\n \"orientation\" : \"portrait\",\n \"idiom\" : \"iphone\",\n \"filename\" : \"splash@2x.png\",\n "
},
{
"path": "Example/GONMarkupParserSample/MainWindow.xib",
"chars": 2265,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/PlaygroundViewController.h",
"chars": 237,
"preview": "//\n// PlaygroundViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyrig"
},
{
"path": "Example/GONMarkupParserSample/PlaygroundViewController.m",
"chars": 1306,
"preview": "//\n// PlaygroundViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyrig"
},
{
"path": "Example/GONMarkupParserSample/PlaygroundViewController.xib",
"chars": 3208,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/ResultViewController.h",
"chars": 323,
"preview": "//\n// ResultViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright ("
},
{
"path": "Example/GONMarkupParserSample/ResultViewController.m",
"chars": 1225,
"preview": "//\n// ResultViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright ("
},
{
"path": "Example/GONMarkupParserSample/ResultViewController.xib",
"chars": 3307,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/RootViewController.h",
"chars": 270,
"preview": "//\n// RootViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c)"
},
{
"path": "Example/GONMarkupParserSample/RootViewController.m",
"chars": 3767,
"preview": "//\n// RootViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright (c)"
},
{
"path": "Example/GONMarkupParserSample/RootViewController.xib",
"chars": 3161,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/SampleViewController.h",
"chars": 301,
"preview": "//\n// SampleViewController.h\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright ("
},
{
"path": "Example/GONMarkupParserSample/SampleViewController.m",
"chars": 1309,
"preview": "//\n// SampleViewController.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 10/11/14.\n// Copyright ("
},
{
"path": "Example/GONMarkupParserSample/SampleViewController.xib",
"chars": 6217,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
},
{
"path": "Example/GONMarkupParserSample/en.lproj/InfoPlist.strings",
"chars": 45,
"preview": "/* Localized versions of Info.plist keys */\n\n"
},
{
"path": "Example/GONMarkupParserSample/main.m",
"chars": 364,
"preview": "//\n// main.m\n// GONMarkupParserSample\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// Copyright (c) 2014 Nicolas "
},
{
"path": "Example/GONMarkupParserSample/samples.plist",
"chars": 5945,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Example/GONMarkupParserSample.xcodeproj/project.pbxproj",
"chars": 69201,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Example/GONMarkupParserSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 166,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:GONMarkupParser"
},
{
"path": "Example/GONMarkupParserSample.xcworkspace/contents.xcworkspacedata",
"chars": 181,
"preview": "<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:GONMarkupParserSample.xcodeproj'"
},
{
"path": "Example/GONMarkupParserSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Example/GONMarkupParserSampleTests/GONMarkupParserSampleTests-Info.plist",
"chars": 674,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Example/GONMarkupParserSampleTests/GONMarkupParserSampleTests.m",
"chars": 705,
"preview": "//\n// GONMarkupParserSampleTests.m\n// GONMarkupParserSampleTests\n//\n// Created by Nicolas Goutaland on 25/06/14.\n// "
},
{
"path": "Example/GONMarkupParserSampleTests/en.lproj/InfoPlist.strings",
"chars": 45,
"preview": "/* Localized versions of Info.plist keys */\n\n"
},
{
"path": "Example/PodFile",
"chars": 183,
"preview": "source 'https://github.com/CocoaPods/Specs.git'\n\nplatform :ios, '7.0'\n\nabstract_target 'defaults' do\n\tpod 'NSString+Colo"
},
{
"path": "GONMarkupParser.podspec",
"chars": 1413,
"preview": "Pod::Spec.new do |s|\n s.name = \"GONMarkupParser\"\n s.version = \"0.8.2\"\n s.summary = \"NSAttributedStr"
},
{
"path": "LICENSE",
"chars": 1082,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 nicolasgoutaland\n\nPermission is hereby granted, free of charge, to any person "
},
{
"path": "README.md",
"chars": 20508,
"preview": "# GONMarkupParser\nEasily build NSAttributedString from XML/HTML like strings.\n\n## Demo\n. The extraction includes 107 files (321.3 KB), approximately 88.7k tokens, and a symbol index with 2 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.