├── .gitignore ├── Classes ├── AutoHyperlinks │ ├── AHHyperlinkScanner.h │ ├── AHHyperlinkScanner.m │ ├── AHLinkLexer.h │ ├── AHLinkLexer.l │ ├── AHMarkedHyperlink.h │ ├── AHMarkedHyperlink.m │ ├── AutoHyperlinks.h │ ├── Copyright.txt │ └── License.txt ├── CoreTextHyperlinkViewAppDelegate.h ├── CoreTextHyperlinkViewAppDelegate.m ├── CoreTextHyperlinkViewViewController.h ├── CoreTextHyperlinkViewViewController.m ├── JSCoreTextView.h ├── JSCoreTextView.m ├── JSEmailCoreTextView.h ├── JSEmailCoreTextView.m ├── JSTableViewCell.h ├── JSTableViewCell.m ├── JSTableViewController.h ├── JSTableViewController.m ├── JSTwitterCoreTextView.h ├── JSTwitterCoreTextView.m ├── JSWebViewController.h ├── JSWebViewController.m └── JSWebViewController.xib ├── CoreTextHyperlinkView-Info.plist ├── CoreTextHyperlinkView.xcodeproj └── project.pbxproj ├── CoreTextHyperlinkViewViewController.xib ├── CoreTextHyperlinkView_Prefix.pch ├── Default-568h@2x.png ├── LICENSE ├── MainWindow.xib ├── README.mdown └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pbxuser 3 | *.perspectivev3 4 | build/ 5 | .svn/ 6 | *.xcworkspace 7 | xcuserdata/ 8 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHHyperlinkScanner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "AHLinkLexer.h" 29 | 30 | typedef void* yyscan_t; 31 | 32 | extern long AHlex( yyscan_t yyscanner ); 33 | extern long AHlex_init( yyscan_t * ptr_yy_globals ); 34 | extern long AHlex_destroy ( yyscan_t yyscanner ); 35 | extern long AHget_leng ( yyscan_t scanner ); 36 | extern void AHset_in ( FILE * in_str , yyscan_t scanner ); 37 | 38 | typedef struct AH_buffer_state *AH_BUFFER_STATE; 39 | extern void AH_switch_to_buffer(AH_BUFFER_STATE, yyscan_t scanner); 40 | extern AH_BUFFER_STATE AH_scan_string (const char *, yyscan_t scanner); 41 | extern void AH_delete_buffer(AH_BUFFER_STATE, yyscan_t scanner); 42 | 43 | @class AHMarkedHyperlink; 44 | 45 | @interface AHHyperlinkScanner : NSObject 46 | { 47 | NSDictionary *m_urlSchemes; 48 | NSString *m_scanString; 49 | #if !TARGET_OS_IPHONE 50 | NSAttributedString *m_scanAttrString; 51 | #endif 52 | BOOL m_strictChecking; 53 | unsigned long m_scanLocation; 54 | unsigned long m_scanStringLength; 55 | } 56 | 57 | /*! 58 | * @brief Allocs and inits a new lax AHHyperlinkScanner with the given NSString 59 | * 60 | * @param inString the scanner's string 61 | * @return a new AHHyperlinkScanner 62 | */ 63 | + (id)hyperlinkScannerWithString:(NSString *)inString; 64 | 65 | /*! 66 | * @brief Allocs and inits a new strict AHHyperlinkScanner with the given NSString 67 | * 68 | * @param inString the scanner's string 69 | * @return a new AHHyperlinkScanner 70 | */ 71 | + (id)strictHyperlinkScannerWithString:(NSString *)inString; 72 | 73 | #if !TARGET_OS_IPHONE 74 | /*! 75 | * @brief Allocs and inits a new lax AHHyperlinkScanner with the given attributed string 76 | * 77 | * @param inString the scanner's string 78 | * @return a new AHHyperlinkScanner 79 | */ 80 | + (id)hyperlinkScannerWithAttributedString:(NSAttributedString *)inString; 81 | 82 | /*! 83 | * @brief Allocs and inits a new strict AHHyperlinkScanner with the given attributed string 84 | * 85 | * @param inString the scanner's string 86 | * @return a new AHHyperlinkScanner 87 | */ 88 | + (id)strictHyperlinkScannerWithAttributedString:(NSAttributedString *)inString; 89 | #endif 90 | 91 | /*! 92 | * @brief Determine the validity of a given string with a custom strictness 93 | * 94 | * @param inString The string to be verified 95 | * @param useStrictChecking Use strict rules or not 96 | * @param index a pointer to the index the string starts at, for easy incrementing. 97 | * @return Boolean 98 | */ 99 | + (BOOL)isStringValidURI:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus; 100 | 101 | /*! 102 | * @brief Init 103 | * 104 | * Inits a new AHHyperlinkScanner object for a NSString with the set strict checking option. 105 | * 106 | * @param inString the NSString to be scanned. 107 | * @param flag Sets strict checking preference. 108 | * @return A new AHHyperlinkScanner. 109 | */ 110 | - (id)initWithString:(NSString *)inString usingStrictChecking:(BOOL)flag; 111 | 112 | #if !TARGET_OS_IPHONE 113 | /*! 114 | * @brief Init 115 | * 116 | * Inits a new AHHyperlinkScanner object for a NSAttributedString with the set strict checking option. 117 | * 118 | * param inString the NSString to be scanned. 119 | * @param flag Sets strict checking preference. 120 | * @return A new AHHyperlinkScanner. 121 | */ 122 | - (id)initWithAttributedString:(NSAttributedString *)inString usingStrictChecking:(BOOL)flag; 123 | #endif 124 | 125 | /*! 126 | * @brief Determine the validity of the scanner's string using the set strictness 127 | * 128 | * @return Boolean 129 | */ 130 | - (BOOL)isValidURI; 131 | 132 | /*! 133 | * @brief Returns a AHMarkedHyperlink representing the next URI in the scanner's string 134 | * 135 | * @return A new AHMarkedHyperlink. 136 | */ 137 | - (AHMarkedHyperlink *)nextURI; 138 | 139 | /*! 140 | * @brief Fetches all the URIs from the scanner's string 141 | * 142 | * @return An array of AHMarkedHyperlinks representing each matched URL in the string or nil if no matches. 143 | */ 144 | - (NSArray *)allURIs; 145 | 146 | /*! 147 | * @brief Scans a string for links and then inserts HTML A tags to surround the links 148 | * @return An autoreleased NSString. 149 | */ 150 | - (NSString *)linkifiedHTML; 151 | 152 | #if !TARGET_OS_IPHONE 153 | /*! 154 | * @brief Scans an attributed string for URIs then adds the link attribs and objects. 155 | * @param inString The NSAttributedString to be linkified 156 | * @return An autoreleased NSAttributedString. 157 | */ 158 | - (NSAttributedString *)linkifiedString; 159 | #endif 160 | 161 | - (unsigned long)scanLocation; 162 | - (void)setScanLocation:(unsigned int)location; 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHHyperlinkScanner.m: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "AHHyperlinkScanner.h" 29 | #import "AHLinkLexer.h" 30 | #import "AHMarkedHyperlink.h" 31 | 32 | #define DEFAULT_URL_SCHEME @"http://" 33 | #define ENC_INDEX_KEY @"encIndex" 34 | #define ENC_CHAR_KEY @"encChar" 35 | 36 | @interface AHHyperlinkScanner (PRIVATE) 37 | - (NSRange)_longestBalancedEnclosureInRange:(NSRange)inRange; 38 | - (BOOL)_scanString:(NSString *)inString upToCharactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx; 39 | - (BOOL)_scanString:(NSString *)inString charactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx; 40 | @end 41 | 42 | @implementation AHHyperlinkScanner 43 | #pragma mark static variables 44 | static NSCharacterSet *skipSet = nil; 45 | static NSCharacterSet *endSet = nil; 46 | static NSCharacterSet *startSet = nil; 47 | static NSCharacterSet *puncSet = nil; 48 | static NSCharacterSet *hostnameComponentSeparatorSet = nil; 49 | static NSArray *enclosureStartArray = nil; 50 | static NSCharacterSet *enclosureSet = nil; 51 | static NSArray *enclosureStopArray = nil; 52 | static NSArray *encKeys = nil; 53 | 54 | #pragma mark runtime initialization 55 | + (void)initialize 56 | { 57 | if (self == [AHHyperlinkScanner class]){ 58 | NSMutableCharacterSet *mutableSkipSet = [[NSMutableCharacterSet alloc] init]; 59 | [mutableSkipSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 60 | [mutableSkipSet formUnionWithCharacterSet:[NSCharacterSet illegalCharacterSet]]; 61 | [mutableSkipSet formUnionWithCharacterSet:[NSCharacterSet controlCharacterSet]]; 62 | [mutableSkipSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]; 63 | skipSet = [[NSCharacterSet characterSetWithBitmapRepresentation:[mutableSkipSet bitmapRepresentation]] retain]; 64 | [mutableSkipSet release]; 65 | 66 | endSet = [[NSCharacterSet characterSetWithCharactersInString:@"\"',:;>)]}.?!@"] retain]; 67 | 68 | NSMutableCharacterSet *mutableStartSet = [[NSMutableCharacterSet alloc] init]; 69 | [mutableStartSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 70 | [mutableStartSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"\"'.,:; 1U) 177 | stringEnc = NSUTF8StringEncoding; 178 | 179 | if (!(inStringEnc = [inString cStringUsingEncoding:stringEnc])) { 180 | return NO; 181 | } 182 | 183 | 184 | encodedLength = strlen(inStringEnc); // length of the string in utf-8 185 | 186 | // initialize the buffer (flex automatically switches to the buffer in this function) 187 | AHlex_init(&scanner); 188 | buf = AH_scan_string(inStringEnc, scanner); 189 | 190 | // call flex to parse the input 191 | *validStatus = AHlex(scanner); 192 | if(index) *index += AHget_leng(scanner); 193 | 194 | // condition for valid URI's 195 | if(*validStatus == AH_URL_VALID || *validStatus == AH_MAILTO_VALID || *validStatus == AH_FILE_VALID){ 196 | AH_delete_buffer(buf, scanner); //remove the buffer from flex. 197 | buf = NULL; //null the buffer pointer for safty's sake. 198 | 199 | // check that the whole string was matched by flex. 200 | // this prevents silly things like "blah...com" from being seen as links 201 | if(AHget_leng(scanner) == encodedLength){ 202 | AHlex_destroy(scanner); 203 | return YES; 204 | } 205 | // condition for degenerate URL's (A.K.A. URI's sans specifiers), requres strict checking to be NO. 206 | }else if((*validStatus == AH_URL_DEGENERATE || *validStatus == AH_MAILTO_DEGENERATE) && !useStrictChecking){ 207 | AH_delete_buffer(buf, scanner); 208 | buf = NULL; 209 | if(AHget_leng(scanner) == encodedLength){ 210 | AHlex_destroy(scanner); 211 | return YES; 212 | } 213 | // if it ain't vaild, and it ain't degenerate, then it's invalid. 214 | }else{ 215 | AH_delete_buffer(buf, scanner); 216 | buf = NULL; 217 | AHlex_destroy(scanner); 218 | return NO; 219 | } 220 | // default case, if the range checking above fails. 221 | AHlex_destroy(scanner); 222 | return NO; 223 | } 224 | 225 | #pragma mark Accessors 226 | 227 | - (AHMarkedHyperlink *)nextURI 228 | { 229 | NSRange scannedRange; 230 | unsigned long scannedLocation = m_scanLocation; 231 | 232 | // scan upto the next whitespace char so that we don't unnecessarity confuse flex 233 | // otherwise we end up validating urls that look like this "http://www.adium.im/ <--cool" 234 | [self _scanString:m_scanString charactersFromSet:startSet intoRange:nil fromIndex:&scannedLocation]; 235 | 236 | // main scanning loop 237 | while([self _scanString:m_scanString upToCharactersFromSet:skipSet intoRange:&scannedRange fromIndex:&scannedLocation]) { 238 | 239 | // Check for and filter enclosures. We can't add (, [, etc. to the skipSet as they may be in a URI 240 | if([enclosureSet characterIsMember:[m_scanString characterAtIndex:scannedRange.location]]){ 241 | unsigned long encIdx = [enclosureStartArray indexOfObject:[m_scanString substringWithRange:NSMakeRange(scannedRange.location, 1)]]; 242 | NSRange encRange; 243 | if(NSNotFound != encIdx) { 244 | encRange = [m_scanString rangeOfString:[enclosureStopArray objectAtIndex:encIdx] options:NSBackwardsSearch range:scannedRange]; 245 | if(NSNotFound != encRange.location){ 246 | scannedRange.location++; scannedRange.length -= 2; 247 | } 248 | } 249 | } 250 | if(!scannedRange.length) break; 251 | 252 | // Find balanced enclosure chars 253 | NSRange longestEnclosure = [self _longestBalancedEnclosureInRange:scannedRange]; 254 | while (scannedRange.length > 2 && [endSet characterIsMember:[m_scanString characterAtIndex:(scannedRange.location + scannedRange.length - 1)]]) { 255 | if((longestEnclosure.location + longestEnclosure.length) < scannedRange.length){ 256 | scannedRange.length--; 257 | }else break; 258 | } 259 | 260 | // if we have a valid URL then save the scanned string, and make a SHMarkedHyperlink out of it. 261 | // this way, we can preserve things like the matched string (to be converted to a NSURL), 262 | // parent string, its validation status (valid, file, degenerate, etc), and its range in the parent string 263 | AH_URI_VERIFICATION_STATUS validStatus; 264 | NSString *_scanString = nil; 265 | if(3 < scannedRange.length) _scanString = [m_scanString substringWithRange:scannedRange]; 266 | if((3 < scannedRange.length) && [[self class] isStringValidURI:_scanString usingStrict:m_strictChecking fromIndex:&m_scanLocation withStatus:&validStatus]){ 267 | AHMarkedHyperlink *markedLink; 268 | 269 | //insert typical specifiers if the URL is degenerate 270 | switch(validStatus){ 271 | case AH_URL_DEGENERATE: 272 | { 273 | NSString *scheme = DEFAULT_URL_SCHEME; 274 | unsigned long i = 0; 275 | 276 | NSRange firstComponent; 277 | [self _scanString:_scanString 278 | upToCharactersFromSet:hostnameComponentSeparatorSet 279 | intoRange:&firstComponent 280 | fromIndex:&i]; 281 | 282 | if(NSNotFound != firstComponent.location) { 283 | NSString *hostnameScheme = [m_urlSchemes objectForKey:[_scanString substringWithRange:firstComponent]]; 284 | if(hostnameScheme) scheme = hostnameScheme; 285 | } 286 | 287 | _scanString = [scheme stringByAppendingString:_scanString]; 288 | 289 | break; 290 | } 291 | 292 | case AH_MAILTO_DEGENERATE: 293 | _scanString = [@"mailto:" stringByAppendingString:_scanString]; 294 | break; 295 | default: 296 | break; 297 | } 298 | 299 | //make a marked link 300 | markedLink = [[AHMarkedHyperlink alloc] initWithString:_scanString 301 | withValidationStatus:validStatus 302 | parentString:m_scanString 303 | andRange:scannedRange]; 304 | return [markedLink autorelease]; 305 | } 306 | 307 | //step location after scanning a string 308 | NSRange startRange = [m_scanString rangeOfCharacterFromSet:puncSet options:NSLiteralSearch range:scannedRange]; 309 | if (startRange.location != NSNotFound) 310 | m_scanLocation = startRange.location + startRange.length; 311 | else 312 | m_scanLocation += scannedRange.length; 313 | 314 | scannedLocation = m_scanLocation; 315 | } 316 | 317 | // if we're here, then NSScanner hit the end of the string 318 | // set AHStringOffset to the string length here so we avoid potential infinite looping with many trailing spaces. 319 | m_scanLocation = m_scanStringLength; 320 | return nil; 321 | } 322 | 323 | -(NSArray *)allURIs 324 | { 325 | NSMutableArray *rangeArray = [NSMutableArray array]; 326 | AHMarkedHyperlink *markedLink; 327 | unsigned long _holdOffset = m_scanLocation; // store location for later restoration; 328 | m_scanLocation = 0; //set the offset to 0. 329 | 330 | //build an array of marked links. 331 | while((markedLink = [self nextURI])){ 332 | [rangeArray addObject:markedLink]; 333 | } 334 | m_scanLocation = _holdOffset; // reset scanLocation 335 | return rangeArray; 336 | } 337 | 338 | - (NSString *)linkifiedHTML 339 | { 340 | // Scan for links 341 | NSArray *links = [self allURIs]; 342 | 343 | // Convert each link into an tag 344 | NSString *unlinkifiedString = m_scanString; 345 | 346 | #if !TARGET_OS_IPHONE 347 | if (!unlinkifiedString) 348 | { 349 | unlinkifiedString = [m_scanAttrString string]; // We MUST copy this according to the docs 350 | } 351 | #endif 352 | 353 | NSMutableString *buffer = [unlinkifiedString mutableCopy]; 354 | 355 | NSEnumerator *linksEnumerator = [links reverseObjectEnumerator]; 356 | AHMarkedHyperlink *aHyperlink; 357 | while ((aHyperlink = [linksEnumerator nextObject])) 358 | { 359 | NSURL *aURL = [aHyperlink URL]; 360 | if (aURL) 361 | { 362 | NSRange hyperlinkRange = [aHyperlink range]; 363 | 364 | [buffer insertString:@"" atIndex:(hyperlinkRange.location + hyperlinkRange.length)]; 365 | 366 | [buffer insertString:@"\">" atIndex:hyperlinkRange.location]; 367 | [buffer insertString:[aURL absoluteString] atIndex:hyperlinkRange.location]; 368 | [buffer insertString:@"state = (nil == currentLink)? (NSUInteger)currentLink : NSNotFound; 435 | state->itemsPtr = stackbuf; 436 | state->mutationsPtr = (unsigned long *)self; 437 | 438 | return fastEnumCount; 439 | } 440 | 441 | #pragma mark Below Here There Be Private Methods 442 | 443 | - (NSRange)_longestBalancedEnclosureInRange:(NSRange)inRange 444 | { 445 | NSMutableArray *enclosureStack = nil, *enclosureArray = nil; 446 | NSString *matchChar = nil; 447 | NSDictionary *encDict; 448 | unsigned long encScanLocation = inRange.location; 449 | 450 | while(encScanLocation < inRange.length + inRange.location) { 451 | [self _scanString:m_scanString upToCharactersFromSet:enclosureSet intoRange:nil fromIndex:&encScanLocation]; 452 | 453 | if(encScanLocation >= (inRange.location + inRange.length)) break; 454 | 455 | matchChar = [m_scanString substringWithRange:NSMakeRange(encScanLocation, 1)]; 456 | 457 | if([enclosureStartArray containsObject:matchChar]) { 458 | encDict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithUnsignedLong:encScanLocation], matchChar, nil] 459 | forKeys:encKeys]; 460 | if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1]; 461 | [enclosureStack addObject:encDict]; 462 | }else if([enclosureStopArray containsObject:matchChar]) { 463 | NSEnumerator *encEnumerator = [enclosureStack objectEnumerator]; 464 | while ((encDict = [encEnumerator nextObject])) { 465 | unsigned long encTagIndex = [(NSNumber *)[encDict objectForKey:ENC_INDEX_KEY] unsignedLongValue]; 466 | unsigned long encStartIndex = [enclosureStartArray indexOfObjectIdenticalTo:[encDict objectForKey:ENC_CHAR_KEY]]; 467 | if([enclosureStopArray indexOfObjectIdenticalTo:matchChar] == encStartIndex) { 468 | NSRange encRange = NSMakeRange(encTagIndex, encScanLocation - encTagIndex + 1); 469 | if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1]; 470 | if(!enclosureArray) enclosureArray = [NSMutableArray arrayWithCapacity:1]; 471 | [enclosureStack removeObject:encDict]; 472 | [enclosureArray addObject:NSStringFromRange(encRange)]; 473 | break; 474 | } 475 | } 476 | } 477 | if(encScanLocation < inRange.length + inRange.location) 478 | encScanLocation++; 479 | } 480 | return (enclosureArray && [enclosureArray count])? NSRangeFromString([enclosureArray lastObject]) : NSMakeRange(0, 0); 481 | } 482 | 483 | // functional replacement for -[NSScanner scanUpToCharactersFromSet:intoString:] 484 | - (BOOL)_scanString:(NSString *)inString upToCharactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx 485 | { 486 | unichar _curChar; 487 | NSRange _outRange; 488 | unsigned long _scanLength = [inString length]; 489 | unsigned long _idx; 490 | 491 | if(_scanLength <= *idx) return NO; 492 | 493 | // Asorb skipSet 494 | for(_idx = *idx; _scanLength > _idx; _idx++) { 495 | _curChar = [inString characterAtIndex:_idx]; 496 | if(![skipSet characterIsMember:_curChar]) break; 497 | } 498 | 499 | // scanUpTo: 500 | for(*idx = _idx; _scanLength > _idx; _idx++) { 501 | _curChar = [inString characterAtIndex:_idx]; 502 | if([inCharSet characterIsMember:_curChar] || [skipSet characterIsMember:_curChar]) break; 503 | } 504 | 505 | _outRange = NSMakeRange(*idx, _idx - *idx); 506 | *idx = _idx; 507 | 508 | if(_outRange.length) { 509 | if(outRangeRef) *outRangeRef = _outRange; 510 | return YES; 511 | } else { 512 | return NO; 513 | } 514 | } 515 | 516 | // functional replacement for -[NSScanner scanCharactersFromSet:intoString:] 517 | - (BOOL)_scanString:(NSString *)inString charactersFromSet:(NSCharacterSet *)inCharSet intoRange:(NSRange *)outRangeRef fromIndex:(unsigned long *)idx 518 | { 519 | unichar _curChar; 520 | NSRange _outRange; 521 | unsigned long _scanLength = [inString length]; 522 | unsigned long _idx = *idx; 523 | 524 | if(_scanLength <= _idx) return NO; 525 | 526 | // Asorb skipSet 527 | for(_idx = *idx; _scanLength > _idx; _idx++) { 528 | _curChar = [inString characterAtIndex:_idx]; 529 | if(![skipSet characterIsMember:_curChar]) break; 530 | } 531 | 532 | // scanCharacters: 533 | for(*idx = _idx; _scanLength > _idx; _idx++) { 534 | _curChar = [inString characterAtIndex:_idx]; 535 | if(![inCharSet characterIsMember:_curChar]) break; 536 | } 537 | 538 | _outRange = NSMakeRange(*idx, _idx - *idx); 539 | *idx = _idx; 540 | 541 | if(_outRange.length) { 542 | if(outRangeRef) *outRangeRef = _outRange; 543 | return YES; 544 | } else { 545 | return NO; 546 | } 547 | } 548 | @end 549 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHLinkLexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | typedef enum { 29 | AH_URL_INVALID = -1, 30 | AH_URL_VALID = 0, 31 | AH_MAILTO_VALID, 32 | AH_FILE_VALID, 33 | AH_URL_DEGENERATE, 34 | AH_MAILTO_DEGENERATE 35 | } AH_URI_VERIFICATION_STATUS; 36 | 37 | #define YY_EXTRA_TYPE unsigned int 38 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHLinkLexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 4 | * copyright file included with this source distribution. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the AutoHyperlinks Framework nor the 14 | * names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * 29 | * Options used: noyywrap : act as if yywrap always returns 1 30 | * 8bit : always force 8-bit chars. 31 | * caseless : case insensitive lexer 32 | * never-interactive : prevents flex from including some calls to gettty() and such 33 | * -- gives a slight performace gain. 34 | * prefix=... : replace YY_whatever with prefix - avoids symbol collisions 35 | * debug : turns on debugging output (string + accepting rule) 36 | * -- only use while editing rules, and don't commit with this on 37 | * (it generates a lot of unnecessary output and kills performace.) 38 | * 39 | * Variables used: uint AHStringOffset : the position of the pointer, relative to the parent string 40 | * incremented by yyleng at each yylex() call. 41 | * int AHValidShift : Used only in CANONICAL start state 42 | * ensures that yyleng reports whole length of the string, 43 | * without a costly call to yymore(). 44 | */ 45 | #include "AHLinkLexer.h" 46 | %} 47 | 48 | ccTLD (ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw) 49 | sTLD (com|edu|gov|int|mil|net|org|biz|info|name|pro) 50 | uTLD (aero|coop|museum|mobi|cat|jobs|travel) 51 | 52 | TLDs ({ccTLD}|{sTLD}|{uTLD}|arpa|local) 53 | %{ 54 | /*The Unicode standard, version 4.1, table 3-6, says that the highest byte that will occur in a valid UTF-8 sequence is 0xF4.*/ 55 | %} 56 | userAndPass [^:@]+(:[^:@]+)? 57 | singleDomain [_[:alnum:]\x80-\xf4-]+ 58 | 59 | urlPath \/[^[:space:]]* 60 | urlSpec ({singleDomain}\.)+{TLDs}(:[0-9]+)?{urlPath}? 61 | urlCSpec {singleDomain}(\.{singleDomain})*(:[0-9]+)?{urlPath}? 62 | 63 | ipv4address ([0-9]{1,3}\.){3}([0-9]{1,3}) 64 | ipURL {ipv4address}(:[0-9]+)?{urlPath}? 65 | 66 | hex4 [0-9A-Fa-f]{1,4} 67 | ipv6HexSeq {hex4}?(::?{hex4}){1,7} 68 | ipv6HexPart ({ipv6HexSeq})|(::{ipv6HexSeq}) 69 | ipv6Address {ipv6HexPart}({ipv4address})? 70 | ipv6URL \[{ipv6Address}](:[0-9]+)?{urlPath}? 71 | 72 | userAtDomain [^:@\/[:space:]]+\@{singleDomain}(\.{singleDomain})* 73 | mailSpec {userAtDomain}\.{TLDs} 74 | jabberSpec xmpp:{mailSpec}{urlPath}?(\?[[:alnum:]]+[;&][^[:space:]]*)? 75 | aolIMSpec aim:goim\?screenname=[^\ \t\n&]+(&message=.+)? 76 | aolChatSpec aim:gochat\?roomname=[^\ \t\n&]+ 77 | yahooIMSpecOld (ymsgr|yahoo):sendim\?.+ 78 | yahooIMSpecNew (ymsgr|yahoo):(\/\/)?im\?to=.+ 79 | yahooIMSpec {yahooIMSpecOld}|{yahooIMSpecNew} 80 | rdarSpec (rdar|radr|radar|x-radar):\/\/(problems?\/)?[0-9]+(&[0-9]+)* 81 | spotifySpec spotify:(track|album|artist|search|playlist|user|radio):[^<>]+ 82 | gtalkSpec gtalk:(chat|call|gtalk)\?jid={mailSpec}(&from_jid={mailSpec})? 83 | myimSpec myim:(addContact|sendIM)\?(((uID|cID)=[0-9]*&?)|(auto=(true|false)&?))+ 84 | msnSpec msnim:(chat|add|voice|video)\?contact={mailSpec} 85 | dictSpec dict:{urlPath} 86 | 87 | %{ 88 | /* Special patterns to ignore */ 89 | %} 90 | 91 | ignoreable (b\.sc|m\.in) 92 | 93 | %option noyywrap nounput 8bit caseless never-interactive reentrant warn prefix="AH" 94 | 95 | %x CANONICAL 96 | %% 97 | 98 | ({userAndPass}@)?{urlCSpec}|{ipURL}|{ipv6URL} {yyleng += yyextra; 99 | BEGIN INITIAL; 100 | return AH_URL_VALID;} 101 | 102 | .* {BEGIN INITIAL; 103 | return AH_URL_INVALID;} 104 | 105 | {ignoreable} {return AH_URL_INVALID;} 106 | 107 | file:\/\/\/.* {return AH_FILE_VALID;} 108 | 109 | https?:\/\/ | 110 | s?ftp:\/\/ | 111 | feed:\/\/ | 112 | ssh:\/\/ | 113 | telnet:\/\/ | 114 | rts?p:\/\/ | 115 | irc:\/\/ | 116 | nntp:\/\/ | 117 | cifs:\/\/ | 118 | smb:\/\/ | 119 | hydra:\/\/ | 120 | itms:\/\/ | 121 | see:\/\/ | 122 | afp:\/\/ | 123 | adiumxtra:\/\/ | 124 | webcal:\/\/ | 125 | svn(\+ssh)?:\/\/ | 126 | notes:\/\/ | 127 | gopher:\/\/ | 128 | x-man-page:\/\/ {yyextra = yyleng; BEGIN CANONICAL;} 129 | 130 | mailto:{mailSpec} {return AH_MAILTO_VALID;} 131 | {mailSpec} {return AH_MAILTO_DEGENERATE;} 132 | 133 | {urlSpec} {return AH_URL_DEGENERATE;} 134 | 135 | sip:{mailSpec} | 136 | {myimSpec} | 137 | {jabberSpec} | 138 | {gtalkSpec} | 139 | {aolIMSpec} | 140 | {aolChatSpec} | 141 | {yahooIMSpec} | 142 | {rdarSpec} | 143 | {msnSpec} | 144 | {dictSpec} | 145 | {spotifySpec} {return AH_URL_VALID;} 146 | 147 | . {return AH_URL_INVALID;} 148 | %% 149 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHMarkedHyperlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "AHLinkLexer.h" 29 | 30 | 31 | @interface AHMarkedHyperlink : NSObject { 32 | NSRange linkRange; 33 | NSURL *linkURL; 34 | NSString *pString; 35 | AH_URI_VERIFICATION_STATUS urlStatus; 36 | } 37 | 38 | -(id)initWithString:(NSString *)inString withValidationStatus:(AH_URI_VERIFICATION_STATUS)status parentString:(NSString *)pInString andRange:(NSRange)inRange; 39 | -(NSString *)parentString; 40 | -(NSRange)range; 41 | -(NSURL *)URL; 42 | -(AH_URI_VERIFICATION_STATUS)validationStatus; 43 | 44 | -(void)setRange:(NSRange)inRange; 45 | -(void)setURL:(NSURL *)inURL; 46 | -(void)setURLFromString:(NSString *)inString; 47 | -(void)setValidationStatus:(AH_URI_VERIFICATION_STATUS)status; 48 | -(void)setParentString:(NSString *)pInString; 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AHMarkedHyperlink.m: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "AHMarkedHyperlink.h" 29 | 30 | @implementation AHMarkedHyperlink 31 | 32 | #pragma mark init and dealloc 33 | 34 | // one really big init method that does it all... 35 | - (id)initWithString:(NSString *)inString withValidationStatus:(AH_URI_VERIFICATION_STATUS)status parentString:(NSString *)pInString andRange:(NSRange)inRange 36 | { 37 | if((self = [self init])) { 38 | [self setURLFromString:inString]; 39 | linkRange = inRange; 40 | [self setParentString:pInString]; 41 | urlStatus = status; 42 | } 43 | 44 | return self; 45 | } 46 | 47 | - (id)init 48 | { 49 | if((self = [super init])){ 50 | linkURL = nil; 51 | pString = nil; 52 | } 53 | 54 | return self; 55 | } 56 | 57 | - (void)dealloc 58 | { 59 | [linkURL release]; 60 | [pString release]; 61 | 62 | [super dealloc]; 63 | } 64 | 65 | #pragma mark Accessors 66 | 67 | - (NSString *)description 68 | { 69 | return [NSString stringWithFormat:@"<%@: %p> %@", [self class], self, [self URL]]; 70 | } 71 | 72 | - (NSRange)range 73 | { 74 | return linkRange; 75 | } 76 | 77 | - (NSString *)parentString 78 | { 79 | return pString; 80 | } 81 | 82 | - (NSURL *)URL 83 | { 84 | return linkURL; 85 | } 86 | 87 | - (AH_URI_VERIFICATION_STATUS)validationStatus 88 | { 89 | return urlStatus; 90 | } 91 | 92 | #pragma mark Transformers 93 | 94 | - (void)setRange:(NSRange)inRange 95 | { 96 | linkRange = inRange; 97 | } 98 | 99 | - (void)setURL:(NSURL *)inURL 100 | { 101 | if(linkURL != inURL){ 102 | [linkURL release]; 103 | linkURL = [inURL retain]; 104 | } 105 | } 106 | 107 | - (void)setURLFromString:(NSString *)inString 108 | { 109 | NSString *linkString, *preString; 110 | 111 | preString = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, 112 | (CFStringRef)inString, 113 | CFSTR(""), 114 | kCFStringEncodingUTF8); 115 | 116 | linkString = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, 117 | preString? (CFStringRef)preString : (CFStringRef)inString, 118 | (CFStringRef)@"#[]", 119 | NULL, 120 | kCFStringEncodingUTF8); // kCFStringEncodingISOLatin1 ); 121 | 122 | [linkURL release]; 123 | linkURL = [[NSURL alloc] initWithString:linkString]; 124 | 125 | [linkString release]; 126 | if(preString) [preString release]; 127 | } 128 | 129 | - (void)setValidationStatus:(AH_URI_VERIFICATION_STATUS)status 130 | { 131 | urlStatus = status; 132 | } 133 | 134 | - (void)setParentString:(NSString *)pInString 135 | { 136 | if(pString != pInString){ 137 | [pString release]; 138 | pString = [pInString retain]; 139 | } 140 | } 141 | 142 | #pragma mark NSCopying 143 | 144 | - (id)copyWithZone:(NSZone *)zone 145 | { 146 | AHMarkedHyperlink *newLink = [[[self class] allocWithZone:zone] initWithString:[[self URL] absoluteString] 147 | withValidationStatus:[self validationStatus] 148 | parentString:[self parentString] 149 | andRange:[self range]]; 150 | return newLink; 151 | } 152 | 153 | #pragma mark NSComparisonMethods 154 | 155 | #if TARGET_OS_IPHONE 156 | - (BOOL)isEqual:(id)object 157 | { 158 | if([object isKindOfClass:[AHMarkedHyperlink class]] && 159 | [(AHMarkedHyperlink *) object validationStatus] == [self validationStatus] && 160 | [(AHMarkedHyperlink *)object range].location == [self range].location && 161 | [(AHMarkedHyperlink *)object range].length == [self range].length && 162 | [[(AHMarkedHyperlink *)object parentString] isEqual:[self parentString]] && 163 | [[(AHMarkedHyperlink *)object URL] isEqual:[self URL]]) 164 | return YES; 165 | return NO; 166 | } 167 | #endif 168 | 169 | #if !TARGET_OS_IPHONE 170 | - (BOOL)isEqualTo:(id)object 171 | { 172 | if([object isKindOfClass:[AHMarkedHyperlink class]] && 173 | [(AHMarkedHyperlink *) object validationStatus] == [self validationStatus] && 174 | [(AHMarkedHyperlink *)object range].location == [self range].location && 175 | [(AHMarkedHyperlink *)object range].length == [self range].length && 176 | [[(AHMarkedHyperlink *)object parentString] isEqualTo:[self parentString]] && 177 | [[(AHMarkedHyperlink *)object URL] isEqualTo:[self URL]]) 178 | return YES; 179 | return NO; 180 | } 181 | 182 | - (BOOL)doesContain:(id)object 183 | { 184 | if([object isKindOfClass:[NSURL class]]) 185 | return [(NSURL *)object isEqualTo:[self URL]]? YES : NO; 186 | if([object isKindOfClass:[NSString class]]) 187 | return [(NSString *)object isEqualTo:[self parentString]]? YES : NO; 188 | 189 | return NO; 190 | } 191 | 192 | - (BOOL)isLike:(NSString *)aString 193 | { 194 | return [[[self parentString] substringWithRange:[self range]] isLike:aString] || 195 | [[[self URL] absoluteString] isLike:aString]; 196 | } 197 | 198 | - (BOOL)isCaseInsensitiveLike:(NSString *)aString 199 | { 200 | return [[[self parentString] substringWithRange:[self range]] isCaseInsensitiveLike:aString] || 201 | [[[self URL] absoluteString] isCaseInsensitiveLike:aString]; 202 | } 203 | 204 | - (BOOL)isGreaterThan:(id)object 205 | { 206 | if([object isKindOfClass:[AHMarkedHyperlink class]]) 207 | return [[[object parentString] substringWithRange:[object range]] 208 | isGreaterThan:[[self parentString] substringWithRange:[self range]]]? YES : NO; 209 | return NO; 210 | } 211 | 212 | - (BOOL)isLessThan:(id)object 213 | { 214 | if([object isKindOfClass:[NSURL class]]) 215 | return [(NSURL *)object isLessThan:[self URL]]? YES : NO; 216 | if([object isKindOfClass:[NSString class]]) 217 | return [(NSString *)object isLessThan:[self parentString]]? YES : NO; 218 | return NO; 219 | } 220 | 221 | - (BOOL)isGreaterThanOrEqualTo:(id)object 222 | { 223 | return [self isGreaterThan:object] || [self isEqualTo:object]; 224 | } 225 | 226 | - (BOOL)isLessThanOrEqualTo:(id)object 227 | { 228 | return [self isLessThan:object] || [self isEqualTo:object]; 229 | } 230 | #endif 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/AutoHyperlinks.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the AutoHyperlinks Framework nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "AHHyperlinkScanner.h" 29 | #import "AHMarkedHyperlink.h" 30 | -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/Copyright.txt: -------------------------------------------------------------------------------- 1 | AutoHyperlinks Framework 2 | (c) 2004-2008 by the following: 3 | 4 | Colin Barrett 5 | Graham Booker 6 | Jorge Salvador Caffarena 7 | Evan Schoenberg 8 | Augie Fackler 9 | Stephen Holt 10 | Peter Hosey 11 | Adam Iser 12 | Jeffrey Melloy 13 | Toby Peterson 14 | Eric Richie 15 | David Smith -------------------------------------------------------------------------------- /Classes/AutoHyperlinks/License.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 3 | * copyright file included with this source distribution. 4 | * 5 | * Copyright (c) 2004-2008 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of the AutoHyperlinks Framework nor the 16 | * names of its contributors may be used to endorse or promote products 17 | * derived from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | -------------------------------------------------------------------------------- /Classes/CoreTextHyperlinkViewAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // CoreTextHyperlinkViewAppDelegate.h 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 24/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import 37 | 38 | @class CoreTextHyperlinkViewViewController, JSTableViewController; 39 | 40 | @interface CoreTextHyperlinkViewAppDelegate : NSObject { 41 | 42 | UINavigationController *navController; 43 | UIWindow *window; 44 | } 45 | 46 | @property (nonatomic, retain) IBOutlet UIWindow *window; 47 | @property (nonatomic, retain) IBOutlet UINavigationController *navController; 48 | @property (retain, nonatomic) IBOutlet JSTableViewController *tableViewController; 49 | @property (retain, nonatomic) IBOutlet CoreTextHyperlinkViewViewController *viewController; 50 | 51 | - (IBAction)example1:(id)sender; 52 | - (IBAction)example2:(id)sender; 53 | 54 | @end 55 | 56 | -------------------------------------------------------------------------------- /Classes/CoreTextHyperlinkViewAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // CoreTextHyperlinkViewAppDelegate.m 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 24/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "CoreTextHyperlinkViewAppDelegate.h" 37 | #import "CoreTextHyperlinkViewViewController.h" 38 | #import "JSTableViewController.h" 39 | 40 | @implementation CoreTextHyperlinkViewAppDelegate 41 | 42 | @synthesize window; 43 | @synthesize navController; 44 | @synthesize tableViewController; 45 | @synthesize viewController; 46 | 47 | 48 | #pragma mark - 49 | #pragma mark Application lifecycle 50 | 51 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 52 | 53 | [self.window setRootViewController:navController]; 54 | [self.window makeKeyAndVisible]; 55 | 56 | return YES; 57 | } 58 | 59 | 60 | - (void)applicationWillResignActive:(UIApplication *)application { 61 | /* 62 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 63 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 64 | */ 65 | } 66 | 67 | 68 | - (void)applicationDidEnterBackground:(UIApplication *)application { 69 | /* 70 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 71 | If your application supports background execution, called instead of applicationWillTerminate: when the user quits. 72 | */ 73 | } 74 | 75 | 76 | - (void)applicationWillEnterForeground:(UIApplication *)application { 77 | /* 78 | Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background. 79 | */ 80 | } 81 | 82 | 83 | - (void)applicationDidBecomeActive:(UIApplication *)application { 84 | /* 85 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 86 | */ 87 | } 88 | 89 | 90 | - (void)applicationWillTerminate:(UIApplication *)application { 91 | /* 92 | Called when the application is about to terminate. 93 | See also applicationDidEnterBackground:. 94 | */ 95 | } 96 | 97 | 98 | #pragma mark - 99 | #pragma mark Memory management 100 | 101 | - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { 102 | /* 103 | Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later. 104 | */ 105 | } 106 | 107 | 108 | - (void)dealloc 109 | { 110 | self.window = nil; 111 | self.navController = nil; 112 | [tableViewController release]; 113 | [viewController release]; 114 | [super dealloc]; 115 | } 116 | 117 | 118 | - (IBAction)example1:(id)sender { 119 | [self.navController pushViewController:self.viewController animated:YES]; 120 | } 121 | 122 | - (IBAction)example2:(id)sender { 123 | [self.navController pushViewController:self.tableViewController animated:YES]; 124 | } 125 | @end 126 | -------------------------------------------------------------------------------- /Classes/CoreTextHyperlinkViewViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // CoreTextHyperlinkViewViewController.h 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 24/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import 37 | #import "JSCoreTextView.h" 38 | 39 | @class JSTwitterCoreTextView; 40 | 41 | @interface CoreTextHyperlinkViewViewController : UIViewController { 42 | 43 | JSTwitterCoreTextView *_textView; 44 | UIScrollView *_scrollView; 45 | 46 | } 47 | 48 | @end 49 | 50 | -------------------------------------------------------------------------------- /Classes/CoreTextHyperlinkViewViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // CoreTextHyperlinkViewViewController.m 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 24/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "CoreTextHyperlinkViewViewController.h" 37 | #import "JSTwitterCoreTextView.h" 38 | #import "AHMarkedHyperlink.h" 39 | #import "JSWebViewController.h" 40 | 41 | @implementation CoreTextHyperlinkViewViewController 42 | 43 | 44 | 45 | /* 46 | // The designated initializer. Override to perform setup that is required before the view is loaded. 47 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 48 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 49 | if (self) { 50 | // Custom initialization 51 | } 52 | return self; 53 | } 54 | */ 55 | 56 | /* 57 | // Implement loadView to create a view hierarchy programmatically, without using a nib. 58 | - (void)loadView { 59 | } 60 | */ 61 | 62 | 63 | - (void)viewDidLoad 64 | { 65 | [super viewDidLoad]; 66 | 67 | [self.view setBackgroundColor:[UIColor lightGrayColor]]; 68 | 69 | NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. http://google.com Nunc non elit nisl. Morbi consequat ipsum id nisi sodales suscipit. Nunc bibendum purus eget sem pulvinar sed ultrices libero mattis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam non quam lorem. Nulla molestie hendrerit libero et commodo. Sed dignissim aliquam aliquam. Maecenas egestas sem vehicula massa molestie mollis. Morbi vitae accumsan mi. Suspendisse eget orci arcu. Aenean eu fermentum arcu. Cras pulvinar fermentum massa tempus vestibulum. Etiam ullamcorper ligula quis leo scelerisque nec eleifend orci rutrum. Vestibulum enim magna, mattis sit amet mattis ullamcorper, facilisis sed nunc. In eu ligula nisl. Curabitur longlink.com/thatwillwrap/thwholepage/andsuchthings/ quis nibh tortor, sit amet egestas ante. Duis cursus egestas felis, vestibulum tincidunt massa luctus sed. Sed quis eleifend justo.\n\nPraesent mauris ipsum, blandit id volutpat ac, egestas vel ligula. Aenean vel imperdiet nisi. Nullam quis volutpat nunc. Mauris elit dui, tempus et vulputate ut, ultrices vel libero. Vivamus vulputate tellus ut felis hendrerit euismod. Fusce sed magna metus. Fusce volutpat sodales adipiscing. Pellentesque feugiat feugiat aliquet. Donec scelerisque dapibus ultricies. Praesent tempor, velit vel porttitor auctor, urna felis fermentum mauris, vel tincidunt massa arcu vitae lacus.\n\nSed leo purus, malesuada ac rutrum et, accumsan nec neque. Nunc quis lectus dui, ac luctus nulla. Nam faucibus libero eu augue luctus convallis. Fusce eget lectus commodo nisi malesuada rhoncus. Nunc id iaculis magna. Nullam laoreet pharetra vestibulum. Sed et libero lacus. Vestibulum mattis nunc in libero bibendum pretium. Cras vulputate nisi sit amet sapien vehicula sit amet vulputate lectus eleifend. Mauris id mi magna, ut lacinia odio. Vivamus lobortis dictum sapien sit amet laoreet. Sed vestibulum bibendum pharetra. Suspendisse interdum, mauris ut malesuada pellentesque, massa ante consequat sem, convallis tempor justo leo a dolor. Donec odio libero, lacinia vel varius eleifend, ornare id nisi. Quisque vitae lacus urna. Maecenas facilisis risus tempor leo lacinia quis placerat risus ullamcorper. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Proin nulla urna, elementum at laoreet eget, lobortis ac purus. Sed porta porttitor sagittis. Maecenas vehicula aliquet elit ac rhoncus.\n\nCras blandit commodo semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam urna libero, fermentum sed mollis quis, pulvinar sed sem. Proin bibendum tempor elit id viverra. Maecenas ut mauris erat. Duis consequat luctus est, nec dictum massa vestibulum ut. Nunc rhoncus, nisl quis ornare hendrerit, nisi risus lobortis justo, vitae malesuada ante augue quis sapien. Fusce iaculis lorem eu lorem porta adipiscing. Duis non est libero, semper mollis tellus. Nunc mattis, purus sed aliquam suscipit, nisl urna molestie turpis, a dapibus augue diam et turpis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\n\nCras accumsan consectetur viverra. Nam a orci diam, id condimentum libero. Curabitur purus velit, semper id dignissim in, vehicula a elit. Ut aliquet nibh sed erat scelerisque nec luctus augue iaculis. Mauris vitae diam laoreet massa hendrerit ullamcorper id in libero. Aliquam ante urna, dignissim nec tempor sit amet, consectetur in libero. Nam suscipit semper molestie. Mauris sollicitudin sapien dictum tortor tempus dignissim. Nunc metus sapien, dapibus sed accumsan eget, volutpat in justo. Quisque nec ante ac mauris congue facilisis. Suspendisse a risus in odio faucibus dictum a vitae magna. Nullam ultrices, google.com/?q=hello%20world erat et tempus tempor, orci velit volutpat urna, vehicula viverra magna libero fringilla arcu. Aliquam erat volutpat. Ut non est et metus tempus aliquet. Sed massa ante, aliquet eget vestibulum nec, laoreet in lacus. In eget dui quis arcu blandit aliquam vitae id ligula. Nam nec sapien vel nulla feugiat cursus. Donec nec enim ante, non congue nulla. Nam mattis placerat mattis. 1234"; 70 | NSString *font = @"Helvetica"; 71 | CGFloat size = 18.0; 72 | CGFloat paddingTop = 10.0; 73 | CGFloat paddingLeft = 10.0; 74 | 75 | _textView = [[[JSTwitterCoreTextView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 0)] autorelease]; 76 | [_textView setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; 77 | [_textView setDelegate:self]; 78 | [_textView setText:text]; 79 | [_textView setFontName:font]; 80 | [_textView setFontSize:size]; 81 | [_textView setHighlightColor:[UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]]; 82 | [_textView setBackgroundColor:[UIColor clearColor]]; 83 | [_textView setPaddingTop:paddingTop]; 84 | [_textView setPaddingLeft:paddingLeft]; 85 | 86 | CGFloat height = [JSCoreTextView measureFrameHeightForText:text 87 | fontName:font 88 | fontSize:size 89 | constrainedToWidth:_textView.frame.size.width - (paddingLeft * 2) 90 | paddingTop:paddingTop 91 | paddingLeft:paddingLeft]; 92 | CGRect textFrame = [_textView frame]; 93 | textFrame.size.height = height; 94 | [_textView setFrame:textFrame]; 95 | 96 | 97 | _scrollView = [[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)] autorelease]; 98 | [_scrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 99 | [_scrollView setContentSize:_textView.frame.size]; 100 | [_scrollView addSubview:_textView]; 101 | 102 | [self.view addSubview:_scrollView]; 103 | 104 | UISegmentedControl *segControl = (UISegmentedControl *)[self.navigationItem titleView]; 105 | [segControl setSelectedSegmentIndex:3]; 106 | [segControl addTarget:self 107 | action:@selector(segmentedControlValueChanged:) 108 | forControlEvents:UIControlEventValueChanged]; 109 | } 110 | 111 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 112 | { 113 | return YES; 114 | } 115 | 116 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 117 | { 118 | 119 | } 120 | 121 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 122 | { 123 | CGFloat height = [JSCoreTextView measureFrameHeightForText:_textView.text 124 | fontName:_textView.fontName 125 | fontSize:_textView.fontSize 126 | constrainedToWidth:_textView.frame.size.width - _textView.paddingLeft * 2 127 | paddingTop:_textView.paddingTop 128 | paddingLeft:_textView.paddingLeft]; 129 | CGRect textFrame = [_textView frame]; 130 | textFrame.size.height = height; 131 | [_textView setFrame:textFrame]; 132 | 133 | [_scrollView setContentSize:_textView.frame.size]; 134 | 135 | [_textView setNeedsDisplay]; 136 | } 137 | 138 | - (void)textView:(JSCoreTextView *)textView linkTapped:(AHMarkedHyperlink *)link 139 | { 140 | JSWebViewController *webViewController = [[[JSWebViewController alloc] initWithNibName:@"JSWebViewController" bundle:nil] autorelease]; 141 | [webViewController setUrl:[link URL]]; 142 | UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:webViewController] autorelease]; 143 | [navController setModalPresentationStyle:UIModalPresentationFullScreen]; 144 | [navController setModalTransitionStyle:UIModalTransitionStyleCoverVertical]; 145 | 146 | [self presentViewController:navController animated:YES completion:NULL]; 147 | } 148 | 149 | - (void)segmentedControlValueChanged:(id)sender 150 | { 151 | UISegmentedControl *segControl = (UISegmentedControl *)sender; 152 | 153 | switch ([segControl selectedSegmentIndex]) { 154 | case 0: 155 | [_textView setFontSize:12.0]; 156 | break; 157 | case 1: 158 | [_textView setFontSize:14.0]; 159 | break; 160 | case 2: 161 | [_textView setFontSize:16.0]; 162 | break; 163 | case 3: 164 | [_textView setFontSize:18.0]; 165 | break; 166 | case 4: 167 | [_textView setFontSize:20.0]; 168 | break; 169 | case 5: 170 | [_textView setFontSize:22.0]; 171 | break; 172 | default: 173 | break; 174 | } 175 | 176 | CGFloat height = [JSCoreTextView measureFrameHeightForText:_textView.text 177 | fontName:_textView.fontName 178 | fontSize:_textView.fontSize 179 | constrainedToWidth:_textView.frame.size.width - _textView.paddingLeft * 2 180 | paddingTop:_textView.paddingTop 181 | paddingLeft:_textView.paddingLeft]; 182 | CGRect textFrame = [_textView frame]; 183 | textFrame.size.height = height; 184 | [_textView setFrame:textFrame]; 185 | 186 | [_scrollView setContentSize:_textView.frame.size]; 187 | } 188 | 189 | - (void)viewDidUnload 190 | { 191 | 192 | } 193 | 194 | 195 | - (void)dealloc 196 | { 197 | [super dealloc]; 198 | } 199 | 200 | @end 201 | -------------------------------------------------------------------------------- /Classes/JSCoreTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSCoreTextView.h 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 16/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import 37 | #import 38 | #import "AHMarkedHyperlink.h" 39 | 40 | @class JSCoreTextView; 41 | 42 | @protocol JSCoreTextViewDelegate 43 | 44 | @optional 45 | - (void)textView:(JSCoreTextView *)textView linkTapped:(AHMarkedHyperlink *)link; 46 | 47 | @end 48 | 49 | 50 | @interface JSCoreTextView : UIView { 51 | 52 | id _delegate; 53 | 54 | CTFramesetterRef _framesetter; 55 | CTFrameRef _frame; 56 | NSString *_text; 57 | 58 | CTTextAlignment _textAlignment; 59 | 60 | 61 | NSArray *_links; 62 | AHMarkedHyperlink *_touchedLink; 63 | AHMarkedHyperlink *_linkToCopy; 64 | CGPoint _linkLocation; 65 | 66 | UIColor *_textColor; 67 | UIColor *_linkColor; 68 | UIColor *_highlightedLinkColor; 69 | UIColor *_highlightColor; 70 | 71 | NSString *_fontName; 72 | CGFloat _fontSize; 73 | 74 | CGFloat _paddingTop; 75 | CGFloat _paddingLeft; 76 | 77 | UIImage *_backgroundImage; 78 | CGFloat _bgImageTopStretchCap; 79 | CGFloat _bgImageLeftStretchCap; 80 | 81 | BOOL _underlined; 82 | } 83 | 84 | @property (nonatomic, assign) id delegate; 85 | 86 | @property (nonatomic, copy) NSString *text; 87 | 88 | @property (nonatomic, assign) CTTextAlignment textAlignment; 89 | 90 | @property (nonatomic, copy) NSString *fontName; 91 | @property (nonatomic, assign) CGFloat fontSize; 92 | 93 | @property (nonatomic, retain) UIColor *textColor; 94 | @property (nonatomic, retain) UIColor *linkColor; 95 | @property (nonatomic, retain) UIColor *highlightedLinkColor; 96 | @property (nonatomic, retain) UIColor *highlightColor; 97 | 98 | @property (nonatomic, assign) CGFloat paddingTop; 99 | @property (nonatomic, assign) CGFloat paddingLeft; 100 | 101 | @property (nonatomic, retain) UIImage *backgroundImage; 102 | @property (nonatomic, assign) CGFloat bgImageTopStretchCap; 103 | @property (nonatomic, assign) CGFloat bgImageLeftStretchCap; 104 | 105 | @property (nonatomic) BOOL underlined; 106 | 107 | + (CGFloat)measureFrameHeightForText:(NSString *)text 108 | fontName:(NSString *)fontName 109 | fontSize:(CGFloat)fontSize 110 | constrainedToWidth:(CGFloat)width 111 | paddingTop:(CGFloat)paddingTop 112 | paddingLeft:(CGFloat)paddingLeft; 113 | 114 | - (void)detectLinks; 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /Classes/JSCoreTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSCoreTextView.m 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 16/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "JSCoreTextView.h" 37 | #import 38 | #import 39 | #import "AHHyperlinkScanner.h" 40 | 41 | float const yAdjustmentFactor = 1.3; 42 | 43 | @interface JSCoreTextView () 44 | 45 | - (void)createFramesetter; 46 | - (void)drawInContext:(CGContextRef)ctx bounds:(CGRect)bounds; 47 | - (CGPathRef)newPathForRoundedRect:(CGRect)rect radius:(CGFloat)radius; 48 | - (void)commonInit; 49 | 50 | @end 51 | 52 | @implementation JSCoreTextView 53 | 54 | @synthesize delegate = _delegate; 55 | 56 | @synthesize text = _text; 57 | @synthesize textAlignment = _textAlignment; 58 | 59 | @synthesize fontName = _fontName; 60 | @synthesize fontSize = _fontSize; 61 | 62 | @synthesize textColor = _textColor; 63 | @synthesize linkColor = _linkColor; 64 | @synthesize highlightedLinkColor = _highlightedLinkColor; 65 | @synthesize highlightColor = _highlightColor; 66 | 67 | @synthesize paddingTop = _paddingTop; 68 | @synthesize paddingLeft = _paddingLeft; 69 | 70 | @synthesize backgroundImage = _backgroundImage; 71 | @synthesize bgImageTopStretchCap = _bgImageTopStretchCap; 72 | @synthesize bgImageLeftStretchCap = _bgImageLeftStretchCap; 73 | 74 | @synthesize underlined = _underlined; 75 | 76 | + (CGFloat)measureFrameHeightForText:(NSString *)text 77 | fontName:(NSString *)fontName 78 | fontSize:(CGFloat)fontSize 79 | constrainedToWidth:(CGFloat)width 80 | paddingTop:(CGFloat)paddingTop 81 | paddingLeft:(CGFloat)paddingLeft 82 | { 83 | if (![text length]) 84 | return 0.0; 85 | 86 | CFMutableAttributedStringRef maString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); 87 | 88 | CFAttributedStringBeginEditing(maString); 89 | CFAttributedStringReplaceString(maString, CFRangeMake(0, 0), (CFStringRef)text); 90 | 91 | CTFontRef font = CTFontCreateWithName((CFStringRef)fontName, fontSize, NULL); 92 | 93 | CFAttributedStringSetAttribute(maString, CFRangeMake(0, CFAttributedStringGetLength(maString)), kCTFontAttributeName, font); 94 | 95 | CFAttributedStringEndEditing(maString); 96 | 97 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(maString); 98 | 99 | CFRelease(font); 100 | 101 | CFRange fitRange = CFRangeMake(0,0); 102 | CGSize size = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, CFStringGetLength((CFStringRef)maString)), NULL, CGSizeMake(width,CGFLOAT_MAX), &fitRange); 103 | 104 | CFRelease(maString); 105 | CFRelease(framesetter); 106 | 107 | int returnVal = size.height + (paddingTop * 2) + 1; // the + 1 might be a bit hacky, but it solves an issue where suggestFrameSizeWithContstrains may return a height that *only-just* doesn't 108 | // fit the given text and it's attributes... It doesn't appear to have any adverse effects in every other situation. 109 | 110 | return (CGFloat)returnVal; 111 | } 112 | 113 | #pragma mark - 114 | #pragma mark Lifecycle 115 | 116 | - (id)initWithFrame:(CGRect)frame 117 | { 118 | if ((self = [super initWithFrame:frame])) 119 | { 120 | [self commonInit]; 121 | } 122 | 123 | return self; 124 | } 125 | 126 | - (id)initWithCoder:(NSCoder *)aDecoder 127 | { 128 | if ((self = [super initWithCoder:aDecoder])) 129 | { 130 | [self commonInit]; 131 | } 132 | 133 | return self; 134 | } 135 | 136 | - (void)commonInit { 137 | _textAlignment = kCTLeftTextAlignment; 138 | _links = nil; 139 | 140 | self.backgroundImage = nil; 141 | self.bgImageTopStretchCap = 0.0; 142 | self.bgImageLeftStretchCap = 0.0; 143 | 144 | self.fontName = @"Helvetica"; 145 | self.fontSize = 17.0; 146 | 147 | self.paddingTop = 0; 148 | self.paddingLeft = 0; 149 | 150 | self.textColor = [UIColor blackColor]; 151 | self.linkColor = [UIColor blueColor]; 152 | self.highlightedLinkColor = [UIColor blueColor]; 153 | self.highlightColor = [UIColor grayColor]; 154 | 155 | self.underlined = NO; 156 | 157 | UILongPressGestureRecognizer *longPressHandler = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)] autorelease]; 158 | [self addGestureRecognizer:longPressHandler]; 159 | 160 | [[NSNotificationCenter defaultCenter] addObserver:self 161 | selector:@selector(handleMenuControllerDidHideMenuNotification:) 162 | name:UIMenuControllerDidHideMenuNotification 163 | object:nil]; 164 | } 165 | 166 | - (void)dealloc 167 | { 168 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 169 | 170 | self.text = nil; 171 | 172 | self.textColor = nil; 173 | self.linkColor = nil; 174 | self.highlightedLinkColor = nil; 175 | self.highlightColor = nil; 176 | 177 | self.backgroundImage = nil; 178 | 179 | [_links release], _links = nil; 180 | 181 | if (_framesetter) 182 | { 183 | CFRelease(_framesetter); 184 | _framesetter = NULL; 185 | } 186 | 187 | if (_frame) 188 | { 189 | CFRelease(_frame); 190 | _frame = NULL; 191 | } 192 | 193 | [super dealloc]; 194 | } 195 | 196 | #pragma mark - 197 | #pragma mark Properties 198 | 199 | - (void)setText:(NSString *)text 200 | { 201 | [_text release]; 202 | _text = [text copy]; 203 | 204 | [self detectLinks]; 205 | 206 | [self setNeedsDisplay]; 207 | } 208 | 209 | - (void)setTextAlignment:(CTTextAlignment)textAlignment 210 | { 211 | _textAlignment = textAlignment; 212 | 213 | [self setNeedsDisplay]; 214 | } 215 | 216 | - (void)setFontName:(NSString *)fontName 217 | { 218 | [_fontName release]; 219 | _fontName = [fontName copy]; 220 | 221 | [self setNeedsDisplay]; 222 | } 223 | 224 | - (void)setFontSize:(CGFloat)fontSize 225 | { 226 | _fontSize = fontSize; 227 | 228 | [self setNeedsDisplay]; 229 | } 230 | 231 | - (void)setPaddingTop:(CGFloat)paddingTop 232 | { 233 | _paddingTop = paddingTop; 234 | 235 | [self setNeedsDisplay]; 236 | } 237 | 238 | - (void)setPaddingLeft:(CGFloat)paddingLeft 239 | { 240 | _paddingLeft = paddingLeft; 241 | 242 | [self setNeedsDisplay]; 243 | } 244 | 245 | - (void)setBackgroundImage:(UIImage *)backgroundImage 246 | { 247 | [_backgroundImage release]; 248 | _backgroundImage = [backgroundImage retain]; 249 | 250 | [self setNeedsDisplay]; 251 | } 252 | 253 | - (void)setBgImageTopStretchCap:(CGFloat)topStretchCap 254 | { 255 | _bgImageTopStretchCap = topStretchCap; 256 | 257 | [self setNeedsDisplay]; 258 | } 259 | 260 | - (void)setBgImageLeftStretchCapLeft:(CGFloat)leftStretchCap 261 | { 262 | _bgImageLeftStretchCap = leftStretchCap; 263 | 264 | [self setNeedsDisplay]; 265 | } 266 | 267 | 268 | - (void)setTextColor:(UIColor *)textColor 269 | { 270 | [_textColor release]; 271 | _textColor = [textColor retain]; 272 | 273 | [self setNeedsDisplay]; 274 | } 275 | 276 | - (void)setLinkColor:(UIColor *)linkColor 277 | { 278 | [_linkColor release]; 279 | _linkColor = [linkColor retain]; 280 | 281 | [self setNeedsDisplay]; 282 | } 283 | 284 | - (void)setHighlightedLinkColor:(UIColor *)highlightedLinkColor 285 | { 286 | [_highlightedLinkColor release]; 287 | _highlightedLinkColor = [highlightedLinkColor retain]; 288 | 289 | [self setNeedsDisplay]; 290 | } 291 | 292 | - (void)setHighlightColor:(UIColor *)highlightColor 293 | { 294 | [_highlightColor release]; 295 | _highlightColor = [highlightColor retain]; 296 | 297 | [self setNeedsDisplay]; 298 | } 299 | 300 | #pragma mark - 301 | #pragma mark Link Detection 302 | 303 | - (void)detectLinks 304 | { 305 | if (_links) 306 | { 307 | [_links release]; 308 | } 309 | 310 | AHHyperlinkScanner *scanner = [AHHyperlinkScanner hyperlinkScannerWithString:self.text]; 311 | _links = [[scanner allURIs] copy]; 312 | } 313 | 314 | #pragma mark - 315 | #pragma mark Touch Handling 316 | 317 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 318 | { 319 | UITouch *touch = [touches anyObject]; 320 | CGPoint location = [touch locationInView:self]; 321 | _linkLocation = location; 322 | 323 | location.y += (self.fontSize / yAdjustmentFactor); 324 | location.x -= self.paddingLeft; 325 | 326 | CFArrayRef lines = CTFrameGetLines(_frame); 327 | 328 | CGPoint origins[CFArrayGetCount(lines)]; 329 | CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), origins); 330 | 331 | CTLineRef line = NULL; 332 | CGPoint lineOrigin = CGPointZero; 333 | for (int i= 0; i < CFArrayGetCount(lines); i++) 334 | { 335 | CGPoint origin = origins[i]; 336 | CGPathRef path = CTFrameGetPath(_frame); 337 | CGRect rect = CGPathGetBoundingBox(path); 338 | 339 | CGFloat y = rect.origin.y + rect.size.height - origin.y; 340 | 341 | if ((location.y >= y) && (location.x >= origin.x)) 342 | { 343 | line = CFArrayGetValueAtIndex(lines, i); 344 | lineOrigin = origin; 345 | } 346 | } 347 | 348 | location.x -= lineOrigin.x; 349 | CFIndex index = CTLineGetStringIndexForPosition(line, location); 350 | 351 | CGFloat xOffset = CTLineGetOffsetForStringIndex(line, index, NULL); 352 | 353 | if (fabs(location.x - xOffset) < 10) 354 | { 355 | for (AHMarkedHyperlink *link in _links) 356 | { 357 | if (((index >= [link range].location) && (index <= ([link range].length + [link range].location)))) 358 | { 359 | _touchedLink = link; 360 | [self setNeedsDisplay]; 361 | } 362 | } 363 | } 364 | 365 | if (!_touchedLink) 366 | { 367 | [super touchesBegan:touches withEvent:event]; 368 | } 369 | } 370 | 371 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 372 | { 373 | [super touchesMoved:touches withEvent:event]; 374 | 375 | UITouch *touch = [touches anyObject]; 376 | CGPoint location = [touch locationInView:self]; 377 | location.y += (self.fontSize / yAdjustmentFactor); 378 | location.x -= self.paddingLeft; 379 | 380 | CFArrayRef lines = CTFrameGetLines(_frame); 381 | 382 | CGPoint origins[CFArrayGetCount(lines)]; 383 | CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), origins); 384 | 385 | CTLineRef line = NULL; 386 | CGPoint lineOrigin = CGPointZero; 387 | for (int i= 0; i < CFArrayGetCount(lines); i++) 388 | { 389 | CGPoint origin = origins[i]; 390 | CGPathRef path = CTFrameGetPath(_frame); 391 | CGRect rect = CGPathGetBoundingBox(path); 392 | 393 | CGFloat y = rect.origin.y + rect.size.height - origin.y; 394 | 395 | if ((location.y >= y) && (location.x >= origin.x)) 396 | { 397 | line = CFArrayGetValueAtIndex(lines, i); 398 | lineOrigin = origin; 399 | } 400 | } 401 | 402 | location.x -= lineOrigin.x; 403 | CFIndex index = CTLineGetStringIndexForPosition(line, location); 404 | 405 | _touchedLink = nil; 406 | 407 | for (AHMarkedHyperlink *link in _links) 408 | { 409 | if (((index >= [link range].location) && (index <= ([link range].length + [link range].location)))) 410 | { 411 | _touchedLink = link; 412 | } 413 | } 414 | 415 | [self setNeedsDisplay]; 416 | } 417 | 418 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 419 | { 420 | _touchedLink = nil; 421 | [self setNeedsDisplay]; 422 | } 423 | 424 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 425 | { 426 | if (_touchedLink) 427 | { 428 | if ([self.delegate respondsToSelector:@selector(textView:linkTapped:)]) 429 | { 430 | [self.delegate textView:self linkTapped:[[_touchedLink retain] autorelease]]; 431 | } 432 | } 433 | 434 | _touchedLink = nil; 435 | 436 | [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; 437 | [self setNeedsDisplay]; 438 | } 439 | 440 | - (void)handleLongPress:(UIGestureRecognizer *)recogniser 441 | { 442 | [self becomeFirstResponder]; 443 | 444 | if ([recogniser state] == UIGestureRecognizerStateBegan) 445 | { 446 | if (_touchedLink) 447 | { 448 | _linkToCopy = _touchedLink; 449 | 450 | UIMenuItem *copyLink = [[[UIMenuItem alloc] initWithTitle:@"Copy Link" 451 | action:@selector(copyLink:)] autorelease]; 452 | [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:copyLink]]; 453 | [[UIMenuController sharedMenuController] setTargetRect:CGRectMake(_linkLocation.x, _linkLocation.y - 10, 1, 1) inView:self]; 454 | } 455 | else 456 | { 457 | [[UIMenuController sharedMenuController] setTargetRect:self.frame inView:self.superview]; 458 | } 459 | [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES]; 460 | } 461 | } 462 | 463 | - (BOOL)canBecomeFirstResponder 464 | { 465 | return YES; 466 | } 467 | 468 | - (BOOL)canResignFirstResponder 469 | { 470 | return YES; 471 | } 472 | 473 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender 474 | { 475 | if (action == @selector(copy:) && !_touchedLink) 476 | { 477 | return YES; 478 | } 479 | else if (action == @selector(copyLink:) && _linkToCopy) 480 | { 481 | return YES; 482 | } 483 | 484 | return NO; 485 | } 486 | 487 | - (void)copy:(id)sender 488 | { 489 | if ([[self text] length]) 490 | { 491 | [[UIPasteboard generalPasteboard] setString:[self text]]; 492 | } 493 | 494 | [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; 495 | } 496 | 497 | - (void)copyLink:(id)sender 498 | { 499 | if (_linkToCopy) 500 | { 501 | [[UIPasteboard generalPasteboard] setString:[[_linkToCopy URL] absoluteString]]; 502 | } 503 | 504 | _linkToCopy = nil; 505 | [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; 506 | } 507 | 508 | - (void)handleMenuControllerDidHideMenuNotification:(NSNotification *)note 509 | { 510 | _linkToCopy = nil; 511 | } 512 | 513 | #pragma mark - 514 | #pragma mark Drawing / Layout 515 | 516 | - (void)layoutSubviews 517 | { 518 | [self setNeedsDisplay]; 519 | } 520 | 521 | - (void)createFramesetter 522 | { 523 | if (_framesetter) 524 | { 525 | CFRelease(_framesetter); 526 | _framesetter = NULL; 527 | } 528 | 529 | CFMutableAttributedStringRef maString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); 530 | 531 | CFAttributedStringBeginEditing(maString); 532 | CFAttributedStringReplaceString(maString, CFRangeMake(0, 0), (CFStringRef)self.text); 533 | 534 | CFIndex length = CFAttributedStringGetLength(maString); 535 | 536 | CTFontRef font = CTFontCreateWithName((CFStringRef)self.fontName, self.fontSize, NULL); 537 | CFAttributedStringSetAttribute(maString, CFRangeMake(0, length), kCTFontAttributeName, font); 538 | CFAttributedStringSetAttribute(maString, CFRangeMake(0, length), kCTForegroundColorAttributeName, [self.textColor CGColor]); 539 | 540 | CTTextAlignment alignment = _textAlignment; 541 | CTParagraphStyleSetting _settings[] = {{kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment}}; 542 | CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0])); 543 | CFAttributedStringSetAttribute(maString, CFRangeMake(0, length), kCTParagraphStyleAttributeName, paragraphStyle); 544 | CFRelease(paragraphStyle); 545 | 546 | for (AHMarkedHyperlink *link in _links) 547 | { 548 | NSRange linkRange = [link range]; 549 | CFRange range = CFRangeMake(linkRange.location, linkRange.length); 550 | 551 | if (self.underlined) 552 | { 553 | // Add an underline 554 | CFAttributedStringSetAttribute(maString, range, kCTUnderlineStyleAttributeName, [NSNumber numberWithInt:kCTUnderlineStyleSingle]); 555 | } 556 | 557 | if ([self.linkColor isEqual:self.textColor] == NO) 558 | { 559 | CFAttributedStringSetAttribute(maString, range, kCTForegroundColorAttributeName, [self.linkColor CGColor]); 560 | CFAttributedStringSetAttribute(maString, range, kCTUnderlineColorAttributeName, [self.linkColor CGColor]); 561 | } 562 | 563 | if ([link isEqual:_touchedLink]) 564 | { 565 | CFAttributedStringSetAttribute(maString, range, kCTForegroundColorAttributeName, [self.highlightedLinkColor CGColor]); 566 | CFAttributedStringSetAttribute(maString, range, kCTUnderlineColorAttributeName, [self.highlightedLinkColor CGColor]); 567 | } 568 | } 569 | 570 | CFAttributedStringEndEditing(maString); 571 | 572 | _framesetter = CTFramesetterCreateWithAttributedString(maString); 573 | 574 | CFRelease(maString); 575 | CFRelease(font); 576 | } 577 | 578 | - (void)drawInContext:(CGContextRef)ctx bounds:(CGRect)bounds 579 | { 580 | [self createFramesetter]; 581 | 582 | CGRect frameRect = bounds; 583 | 584 | CGMutablePathRef path = NULL; 585 | 586 | frameRect.size.height = FLT_MAX; 587 | 588 | frameRect.size.height = [[self class] measureFrameHeightForText:self.text 589 | fontName:self.fontName 590 | fontSize:self.fontSize 591 | constrainedToWidth:frameRect.size.width 592 | paddingTop:self.paddingTop 593 | paddingLeft:self.paddingLeft]; 594 | 595 | frameRect.origin.y = CGRectGetMaxY(bounds) - frameRect.size.height; 596 | 597 | path = CGPathCreateMutable(); 598 | CGPathAddRect(path, NULL, frameRect); 599 | 600 | if (_frame) 601 | { 602 | CFRelease(_frame); 603 | _frame = NULL; 604 | } 605 | 606 | _frame = CTFramesetterCreateFrame(_framesetter, CFRangeMake(0, 0), path, NULL); 607 | CGContextSetTextMatrix(ctx, CGAffineTransformIdentity); 608 | 609 | // highlight the link 610 | if (_touchedLink) 611 | { 612 | // get the lines 613 | CFArrayRef lines = CTFrameGetLines(_frame); 614 | 615 | // for each line 616 | for (int i = 0; i < CFArrayGetCount(lines); i++) 617 | { 618 | CTLineRef line = CFArrayGetValueAtIndex(lines, i); 619 | CFArrayRef runs = CTLineGetGlyphRuns(line); 620 | 621 | // fo each glyph run in the line 622 | for (int j = 0; j < CFArrayGetCount(runs); j++) 623 | { 624 | CTRunRef run = CFArrayGetValueAtIndex(runs, j); 625 | 626 | CFRange runRange = CTRunGetStringRange(run); 627 | 628 | // if the range of the glyph run falls within the range of the link to be highlighted 629 | if ((((runRange.location >= [_touchedLink range].location) && (runRange.location < [_touchedLink range].location + [_touchedLink range].length)) && 630 | ((runRange.location + runRange.length) <= ([_touchedLink range].location + [_touchedLink range].length)))) 631 | { 632 | //runRange is within the link range 633 | 634 | CGRect runBounds = CGRectZero; 635 | 636 | // work out the bounding rect for the glyph run (this doesn't include the origin) 637 | CGFloat ascent, descent, leading; 638 | CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading); 639 | runBounds.size.width = width; 640 | runBounds.size.height = ascent + fabsf(descent) + leading; 641 | 642 | // get the origin of the glyph run (this is relative to the origin of the line) 643 | const CGPoint *positions = CTRunGetPositionsPtr(run); 644 | 645 | // get the origins of the lines 646 | CGPoint lineOrigins[CFArrayGetCount(lines)]; 647 | CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), lineOrigins); 648 | 649 | CGRect rect = CGPathGetBoundingBox(path); 650 | CGPoint origin = lineOrigins[i]; 651 | 652 | // set the x position for the glyph run 653 | runBounds.origin.x += positions[0].x; 654 | runBounds.origin.x += origin.x; 655 | runBounds.origin.x += self.paddingLeft; 656 | 657 | // flip the y coordinate from core text coordinate system to work in the native coordinate system (this always confuses the hell out of me) 658 | CGFloat y = rect.origin.y + rect.size.height - origin.y; 659 | runBounds.origin.y += y - (self.fontSize / yAdjustmentFactor); 660 | 661 | // adjust the rect to be slightly bigger than the text 662 | runBounds.origin.x -= self.fontSize / 4; 663 | runBounds.size.width += self.fontSize / 2; 664 | runBounds.origin.y -= self.fontSize / 8; // this is more favourable 665 | runBounds.size.height += self.fontSize / 4; 666 | 667 | //NSLog(@"RunBounds: %@", NSStringFromCGRect(runBounds)); 668 | 669 | // Finally, create a rounded rect with a nice shadow and fill. 670 | CGContextSetFillColorWithColor(ctx, [self.highlightColor CGColor]); 671 | CGPathRef highlightPath = [self newPathForRoundedRect:runBounds radius:(runBounds.size.height / 6)]; 672 | CGContextSetShadow(ctx, CGSizeMake(2, 2), 1.0); 673 | CGContextAddPath(ctx, highlightPath); 674 | CGContextFillPath(ctx); 675 | CGPathRelease(highlightPath); 676 | CGContextSetShadowWithColor(ctx, CGSizeZero, 0.0, NULL); // turn off shadowing 677 | } 678 | } 679 | } 680 | 681 | } 682 | 683 | // flip the coordinate system so that core text doesn't plough forward and draw the text upside down 684 | CGContextTranslateCTM(ctx, 0.0, bounds.size.height); 685 | CGContextScaleCTM(ctx, 1.0, -1.0); 686 | 687 | // draw the CTFrame 688 | CTFrameDraw(_frame, ctx); 689 | 690 | CGPathRelease(path); 691 | } 692 | 693 | - (void)drawRect:(CGRect)rect 694 | { 695 | [super drawRect:rect]; 696 | 697 | CGRect bounds = self.bounds; 698 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 699 | 700 | CGContextFlush(ctx); 701 | 702 | if (self.backgroundImage) 703 | { 704 | UIImage *bg = self.backgroundImage; 705 | 706 | if (self.bgImageTopStretchCap > 0 || self.bgImageLeftStretchCap > 0) 707 | { 708 | bg = [self.backgroundImage stretchableImageWithLeftCapWidth:self.bgImageLeftStretchCap topCapHeight:self.bgImageTopStretchCap]; 709 | } 710 | 711 | [bg drawInRect:rect]; // Using 'bounds' here causes the bubble to be drawn with a white line near the stretching point. 712 | // Using 'rect' fixes it... no idea why. 713 | } 714 | 715 | bounds.size.width -= self.paddingLeft * 2; 716 | bounds.origin.x += self.paddingLeft; 717 | bounds.size.height += self.paddingTop * 2; 718 | bounds.origin.y -= self.paddingTop; 719 | 720 | [self drawInContext:ctx bounds:bounds]; 721 | } 722 | 723 | - (CGPathRef)newPathForRoundedRect:(CGRect)rect radius:(CGFloat)radius 724 | { 725 | CGMutablePathRef retPath = CGPathCreateMutable(); 726 | 727 | CGRect innerRect = CGRectInset(rect, radius, radius); 728 | 729 | CGFloat inside_right = innerRect.origin.x + innerRect.size.width; 730 | CGFloat outside_right = rect.origin.x + rect.size.width; 731 | CGFloat inside_bottom = innerRect.origin.y + innerRect.size.height; 732 | CGFloat outside_bottom = rect.origin.y + rect.size.height; 733 | 734 | CGFloat inside_top = innerRect.origin.y; 735 | CGFloat outside_top = rect.origin.y; 736 | CGFloat outside_left = rect.origin.x; 737 | 738 | CGPathMoveToPoint(retPath, NULL, innerRect.origin.x, outside_top); 739 | 740 | CGPathAddLineToPoint(retPath, NULL, inside_right, outside_top); 741 | CGPathAddArcToPoint(retPath, NULL, outside_right, outside_top, outside_right, inside_top, radius); 742 | CGPathAddLineToPoint(retPath, NULL, outside_right, inside_bottom); 743 | CGPathAddArcToPoint(retPath, NULL, outside_right, outside_bottom, inside_right, outside_bottom, radius); 744 | 745 | CGPathAddLineToPoint(retPath, NULL, innerRect.origin.x, outside_bottom); 746 | CGPathAddArcToPoint(retPath, NULL, outside_left, outside_bottom, outside_left, inside_bottom, radius); 747 | CGPathAddLineToPoint(retPath, NULL, outside_left, inside_top); 748 | CGPathAddArcToPoint(retPath, NULL, outside_left, outside_top, innerRect.origin.x, outside_top, radius); 749 | 750 | CGPathCloseSubpath(retPath); 751 | 752 | return retPath; 753 | } 754 | 755 | @end 756 | -------------------------------------------------------------------------------- /Classes/JSEmailCoreTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Ben Baron (Einstein Times Two Software). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSEmailCoreTextView.h 30 | // 31 | // Created by Ben Baron on 06/06/2012. 32 | // Copyright 2012 Ben Baron. All rights reserved. 33 | // 34 | 35 | #import "JSCoreTextView.h" 36 | 37 | @interface JSEmailCoreTextView : JSCoreTextView 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/JSEmailCoreTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2012 Ben Baron (Einstein Times Two Software). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSEmailCoreTextView.m 30 | // 31 | // Created by Ben Baron on 06/06/2012. 32 | // Copyright 2012 Ben Baron. All rights reserved. 33 | // 34 | 35 | #import "JSEmailCoreTextView.h" 36 | #import "AHMarkedHyperlink.h" 37 | 38 | @implementation JSEmailCoreTextView 39 | 40 | - (void)detectLinks 41 | { 42 | [super detectLinks]; 43 | 44 | if (![[self text] length]) 45 | { 46 | return; 47 | } 48 | 49 | NSMutableArray *tempLinks = [_links mutableCopy]; 50 | 51 | NSArray *expressions = [[[NSArray alloc] initWithObjects:@"([a-zA-Z0-9_]@[a-zA-Z0-9_]+)", nil] autorelease]; 52 | //get emails 53 | for (NSString *expression in expressions) 54 | { 55 | NSError *error = NULL; 56 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression 57 | options:NSRegularExpressionCaseInsensitive 58 | error:&error]; 59 | NSArray *matches = [regex matchesInString:[self text] 60 | options:0 61 | range:NSMakeRange(0, [[self text] length])]; 62 | 63 | NSString *matchedString = nil; 64 | for (NSTextCheckingResult *match in matches) 65 | { 66 | matchedString = [[[self text] substringWithRange:[match range]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 67 | 68 | NSString *email = [matchedString substringFromIndex:1]; 69 | 70 | AHMarkedHyperlink *hyperlink = [[[AHMarkedHyperlink alloc] initWithString:[NSString stringWithFormat:@"mailto:%@", email] 71 | withValidationStatus:AH_URL_VALID 72 | parentString:[self text] 73 | andRange:[match range]] autorelease]; 74 | [tempLinks addObject:hyperlink]; 75 | } 76 | } 77 | 78 | [_links release]; 79 | _links = [tempLinks copy]; 80 | [tempLinks release], tempLinks = nil; 81 | } 82 | 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Classes/JSTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSTableViewCell.h 3 | // CoreTextHyperlinkView 4 | // 5 | // Created by James Addyman on 11/03/2013. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class JSCoreTextView; 12 | 13 | @interface JSTableViewCell : UITableViewCell 14 | 15 | @property (nonatomic, readonly) JSCoreTextView *coreTextView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Classes/JSTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSTableViewCell.m 3 | // CoreTextHyperlinkView 4 | // 5 | // Created by James Addyman on 11/03/2013. 6 | // 7 | // 8 | 9 | #import "JSTableViewCell.h" 10 | #import "JSCoreTextView.h" 11 | 12 | @implementation JSTableViewCell 13 | 14 | @synthesize coreTextView = _coreTextView; 15 | 16 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 17 | { 18 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 19 | if (self) { 20 | _coreTextView = [[JSCoreTextView alloc] initWithFrame:[[self contentView] bounds]]; 21 | [_coreTextView setBackgroundColor:[UIColor whiteColor]]; 22 | [_coreTextView setPaddingTop:12]; 23 | [_coreTextView setText:@"Hello, this is a link http://google.com"]; 24 | [self.contentView addSubview:_coreTextView]; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)dealloc 30 | { 31 | [_coreTextView release], _coreTextView = nil; 32 | [super dealloc]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Classes/JSTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSTableViewController.h 3 | // CoreTextHyperlinkView 4 | // 5 | // Created by James Addyman on 11/03/2013. 6 | // 7 | // 8 | 9 | #import 10 | #import "JSCoreTextView.h" 11 | 12 | @interface JSTableViewController : UITableViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Classes/JSTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSTableViewController.m 3 | // CoreTextHyperlinkView 4 | // 5 | // Created by James Addyman on 11/03/2013. 6 | // 7 | // 8 | 9 | #import "JSTableViewController.h" 10 | #import "JSTableViewCell.h" 11 | #import "JSCoreTextView.h" 12 | 13 | @interface JSTableViewController () 14 | 15 | @end 16 | 17 | @implementation JSTableViewController 18 | 19 | - (id)initWithStyle:(UITableViewStyle)style 20 | { 21 | self = [super initWithStyle:style]; 22 | if (self) { 23 | // Custom initialization 24 | } 25 | return self; 26 | } 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | 32 | // Uncomment the following line to preserve selection between presentations. 33 | // self.clearsSelectionOnViewWillAppear = NO; 34 | 35 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 36 | // self.navigationItem.rightBarButtonItem = self.editButtonItem; 37 | } 38 | 39 | - (void)didReceiveMemoryWarning 40 | { 41 | [super didReceiveMemoryWarning]; 42 | // Dispose of any resources that can be recreated. 43 | } 44 | 45 | #pragma mark - Table view data source 46 | 47 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 48 | { 49 | return 1; 50 | } 51 | 52 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 53 | { 54 | return 50; 55 | } 56 | 57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 58 | { 59 | static NSString *CellIdentifier = @"Cell"; 60 | JSTableViewCell *cell = (JSTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 61 | if (!cell) 62 | { 63 | cell = [[[JSTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 64 | } 65 | 66 | [[cell coreTextView] setDelegate:self]; 67 | 68 | return cell; 69 | } 70 | 71 | #pragma mark - Table view delegate 72 | 73 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 74 | { 75 | } 76 | 77 | #pragma mark - Core Text View Delegate 78 | 79 | - (void)textView:(JSCoreTextView *)textView linkTapped:(AHMarkedHyperlink *)link 80 | { 81 | if ([link validationStatus] == AH_URL_VALID) 82 | { 83 | NSLog(@"Link URL: %@", [link URL]); 84 | } 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /Classes/JSTwitterCoreTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSTwitterCoreTextView.h 30 | // Interstate 31 | // 32 | // Created by James Addyman on 06/01/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "JSCoreTextView.h" 37 | 38 | @interface JSTwitterCoreTextView : JSCoreTextView { 39 | 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Classes/JSTwitterCoreTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSTwitterCoreTextView.m 30 | // Interstate 31 | // 32 | // Created by James Addyman on 06/01/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "JSTwitterCoreTextView.h" 37 | #import "AHMarkedHyperlink.h" 38 | 39 | @implementation JSTwitterCoreTextView 40 | 41 | - (void)detectLinks 42 | { 43 | [super detectLinks]; 44 | 45 | if (![[self text] length]) 46 | { 47 | return; 48 | } 49 | 50 | NSMutableArray *tempLinks = [_links mutableCopy]; 51 | NSArray *expressions = [[[NSArray alloc] initWithObjects:@"(@[a-zA-Z0-9_]+/[a-zA-Z]{1}\\S*)", // lists 52 | @"(@[a-zA-Z0-9_]+)", // screen names 53 | @"(#[a-zA-Z0-9_-]+)", // hash tags 54 | nil] autorelease]; 55 | //get @list/full-names, #hashtags and @usernames 56 | for (NSString *expression in expressions) 57 | { 58 | NSError *error = NULL; 59 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression 60 | options:NSRegularExpressionCaseInsensitive 61 | error:&error]; 62 | NSArray *matches = [regex matchesInString:[self text] 63 | options:0 64 | range:NSMakeRange(0, [[self text] length])]; 65 | 66 | NSString *matchedString = nil; 67 | for (NSTextCheckingResult *match in matches) 68 | { 69 | // if this string is already part of a link, don't create a new link 70 | BOOL stringAlreadyLinked = NO; 71 | for (AHMarkedHyperlink *link in tempLinks) { 72 | if (NSIntersectionRange(match.range, link.range).length > 0) stringAlreadyLinked = YES; 73 | } 74 | if (stringAlreadyLinked) continue; 75 | 76 | matchedString = [[[self text] substringWithRange:[match range]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 77 | 78 | if ([matchedString hasPrefix:@"@"] && [matchedString rangeOfString:@"/"].location != NSNotFound) { // lists 79 | NSString *list = [matchedString substringFromIndex:1]; 80 | 81 | AHMarkedHyperlink *hyperlink = [[[AHMarkedHyperlink alloc] initWithString:[NSString stringWithFormat:@"http://twitter.com/%@", list] 82 | withValidationStatus:AH_URL_VALID parentString:[self text] 83 | andRange:[match range]] autorelease]; 84 | [tempLinks addObject:hyperlink]; 85 | } 86 | else if ([matchedString hasPrefix:@"@"]) // usernames 87 | { 88 | NSString *username = [matchedString substringFromIndex:1]; 89 | 90 | AHMarkedHyperlink *hyperlink = [[[AHMarkedHyperlink alloc] initWithString:[NSString stringWithFormat:@"http://twitter.com/%@", username] 91 | withValidationStatus:AH_URL_VALID 92 | parentString:[self text] 93 | andRange:[match range]] autorelease]; 94 | [tempLinks addObject:hyperlink]; 95 | } 96 | else if ([matchedString hasPrefix:@"#"]) // hash tag 97 | { 98 | NSString *searchTerm = [[matchedString substringFromIndex:1] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 99 | 100 | AHMarkedHyperlink *hyperlink = [[[AHMarkedHyperlink alloc] initWithString:[NSString stringWithFormat:@"http://twitter.com/search?q=%@", searchTerm] 101 | withValidationStatus:AH_URL_VALID 102 | parentString:[self text] 103 | andRange:[match range]] autorelease]; 104 | [tempLinks addObject:hyperlink]; 105 | } 106 | } 107 | } 108 | 109 | [_links release]; 110 | _links = [tempLinks copy]; 111 | [tempLinks release], tempLinks = nil; 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /Classes/JSWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSWebViewController.h 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James on 13/02/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import 37 | 38 | 39 | @interface JSWebViewController : UIViewController { 40 | 41 | UIWebView *_webView; 42 | 43 | NSURL *_url; 44 | } 45 | 46 | @property (nonatomic, retain) IBOutlet UIWebView *webView; 47 | @property (nonatomic, retain) NSURL *url; 48 | 49 | - (void)done:(id)sender; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Classes/JSWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // JSWebViewController.m 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James on 13/02/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import "JSWebViewController.h" 37 | 38 | 39 | @implementation JSWebViewController 40 | 41 | @synthesize webView = _webView; 42 | @synthesize url = _url; 43 | 44 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 45 | { 46 | if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) 47 | { 48 | 49 | } 50 | 51 | return self; 52 | } 53 | 54 | - (void)dealloc 55 | { 56 | self.url = nil; 57 | self.webView = nil; 58 | 59 | [super dealloc]; 60 | } 61 | 62 | - (void)didReceiveMemoryWarning 63 | { 64 | [super didReceiveMemoryWarning]; 65 | } 66 | 67 | #pragma mark - View lifecycle 68 | 69 | - (void)viewDidLoad 70 | { 71 | [super viewDidLoad]; 72 | 73 | [self.navigationItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone 74 | target:self 75 | action:@selector(done:)] autorelease]]; 76 | 77 | 78 | if (self.url) 79 | { 80 | [self.webView loadRequest:[NSURLRequest requestWithURL:self.url]]; 81 | } 82 | } 83 | 84 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 85 | { 86 | return YES; 87 | } 88 | 89 | - (void)setUrl:(NSURL *)url 90 | { 91 | [_url release]; 92 | _url = [url retain]; 93 | 94 | [self.webView loadRequest:[NSURLRequest requestWithURL:_url]]; 95 | } 96 | 97 | - (void)done:(id)sender 98 | { 99 | [self.parentViewController dismissViewControllerAnimated:YES completion:NULL]; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Classes/JSWebViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1056 5 | 10J567 6 | 1294 7 | 1038.35 8 | 462.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 294 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIView 17 | IBUIWebView 18 | 19 | 20 | YES 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | YES 25 | 26 | YES 27 | 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | 44 | YES 45 | 46 | 47 | 274 48 | {320, 416} 49 | 50 | 51 | 52 | 1 53 | MSAxIDEAA 54 | 55 | IBCocoaTouchFramework 56 | 1 57 | YES 58 | 59 | 60 | {{0, 64}, {320, 416}} 61 | 62 | 63 | 64 | 3 65 | MQA 66 | 67 | 2 68 | 69 | 70 | 71 | 72 | NO 73 | 74 | IBCocoaTouchFramework 75 | 76 | 77 | 78 | 79 | YES 80 | 81 | 82 | view 83 | 84 | 85 | 86 | 3 87 | 88 | 89 | 90 | delegate 91 | 92 | 93 | 94 | 5 95 | 96 | 97 | 98 | webView 99 | 100 | 101 | 102 | 6 103 | 104 | 105 | 106 | 107 | YES 108 | 109 | 0 110 | 111 | 112 | 113 | 114 | 115 | 1 116 | 117 | 118 | YES 119 | 120 | 121 | 122 | 123 | 124 | -1 125 | 126 | 127 | File's Owner 128 | 129 | 130 | -2 131 | 132 | 133 | 134 | 135 | 4 136 | 137 | 138 | 139 | 140 | 141 | 142 | YES 143 | 144 | YES 145 | -1.CustomClassName 146 | -2.CustomClassName 147 | 1.IBEditorWindowLastContentRect 148 | 1.IBPluginDependency 149 | 4.IBPluginDependency 150 | 151 | 152 | YES 153 | JSWebViewController 154 | UIResponder 155 | {{556, 412}, {320, 480}} 156 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 157 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 158 | 159 | 160 | 161 | YES 162 | 163 | 164 | 165 | 166 | 167 | YES 168 | 169 | 170 | 171 | 172 | 6 173 | 174 | 175 | 176 | YES 177 | 178 | JSWebViewController 179 | UIViewController 180 | 181 | webView 182 | UIWebView 183 | 184 | 185 | webView 186 | 187 | webView 188 | UIWebView 189 | 190 | 191 | 192 | IBProjectSource 193 | ./Classes/JSWebViewController.h 194 | 195 | 196 | 197 | 198 | 0 199 | IBCocoaTouchFramework 200 | 201 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 202 | 203 | 204 | YES 205 | 3 206 | 294 207 | 208 | 209 | -------------------------------------------------------------------------------- /CoreTextHyperlinkView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | MainWindow 29 | 30 | 31 | -------------------------------------------------------------------------------- /CoreTextHyperlinkView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A4DCD4016EE796A00271051 /* JSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A4DCD3F16EE796A00271051 /* JSTableViewController.m */; }; 11 | 1A4DCD4316EE798D00271051 /* JSTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A4DCD4216EE798D00271051 /* JSTableViewCell.m */; }; 12 | 1A4DCD4516EE80EB00271051 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1A4DCD4416EE80EB00271051 /* Default-568h@2x.png */; }; 13 | 1A62C44012D5E6EA00003E1E /* JSTwitterCoreTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A62C43F12D5E6EA00003E1E /* JSTwitterCoreTextView.m */; }; 14 | 1A7A8B3512C4C945003283E5 /* JSCoreTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A7A8B3412C4C945003283E5 /* JSCoreTextView.m */; }; 15 | 1A7A8B4712C4C9C6003283E5 /* AHHyperlinkScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A7A8B4112C4C9C6003283E5 /* AHHyperlinkScanner.m */; }; 16 | 1A7A8B4812C4C9C6003283E5 /* AHLinkLexer.l in Sources */ = {isa = PBXBuildFile; fileRef = 1A7A8B4312C4C9C6003283E5 /* AHLinkLexer.l */; }; 17 | 1A7A8B4912C4C9C6003283E5 /* AHMarkedHyperlink.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A7A8B4512C4C9C6003283E5 /* AHMarkedHyperlink.m */; }; 18 | 1A7A8B5712C4CA10003283E5 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A7A8B5612C4CA10003283E5 /* CoreText.framework */; }; 19 | 1ACBF8161307576E0050711A /* JSWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACBF8141307576E0050711A /* JSWebViewController.m */; }; 20 | 1ACBF8171307576E0050711A /* JSWebViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1ACBF8151307576E0050711A /* JSWebViewController.xib */; }; 21 | 1D3623260D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.m */; }; 22 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 23 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 24 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 25 | 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; 26 | 2899E5220DE3E06400AC0155 /* CoreTextHyperlinkViewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2899E5210DE3E06400AC0155 /* CoreTextHyperlinkViewViewController.xib */; }; 27 | 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; 28 | 28D7ACF80DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.m */; }; 29 | 53D809CD15801FBC00F4CD19 /* JSEmailCoreTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D809CC15801FBC00F4CD19 /* JSEmailCoreTextView.m */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1A4DCD3E16EE796A00271051 /* JSTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTableViewController.h; sourceTree = ""; }; 34 | 1A4DCD3F16EE796A00271051 /* JSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSTableViewController.m; sourceTree = ""; }; 35 | 1A4DCD4116EE798D00271051 /* JSTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTableViewCell.h; sourceTree = ""; }; 36 | 1A4DCD4216EE798D00271051 /* JSTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSTableViewCell.m; sourceTree = ""; }; 37 | 1A4DCD4416EE80EB00271051 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 38 | 1A62C43E12D5E6EA00003E1E /* JSTwitterCoreTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTwitterCoreTextView.h; sourceTree = ""; }; 39 | 1A62C43F12D5E6EA00003E1E /* JSTwitterCoreTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSTwitterCoreTextView.m; sourceTree = ""; }; 40 | 1A7A8B3312C4C945003283E5 /* JSCoreTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCoreTextView.h; sourceTree = ""; }; 41 | 1A7A8B3412C4C945003283E5 /* JSCoreTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCoreTextView.m; sourceTree = ""; }; 42 | 1A7A8B4012C4C9C6003283E5 /* AHHyperlinkScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHHyperlinkScanner.h; sourceTree = ""; }; 43 | 1A7A8B4112C4C9C6003283E5 /* AHHyperlinkScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHHyperlinkScanner.m; sourceTree = ""; }; 44 | 1A7A8B4212C4C9C6003283E5 /* AHLinkLexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHLinkLexer.h; sourceTree = ""; }; 45 | 1A7A8B4312C4C9C6003283E5 /* AHLinkLexer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; path = AHLinkLexer.l; sourceTree = ""; }; 46 | 1A7A8B4412C4C9C6003283E5 /* AHMarkedHyperlink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHMarkedHyperlink.h; sourceTree = ""; }; 47 | 1A7A8B4512C4C9C6003283E5 /* AHMarkedHyperlink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHMarkedHyperlink.m; sourceTree = ""; }; 48 | 1A7A8B4612C4C9C6003283E5 /* AutoHyperlinks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoHyperlinks.h; sourceTree = ""; }; 49 | 1A7A8B5612C4CA10003283E5 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; 50 | 1ACBF8131307576E0050711A /* JSWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebViewController.h; sourceTree = ""; }; 51 | 1ACBF8141307576E0050711A /* JSWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSWebViewController.m; sourceTree = ""; }; 52 | 1ACBF8151307576E0050711A /* JSWebViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = JSWebViewController.xib; path = Classes/JSWebViewController.xib; sourceTree = ""; }; 53 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 54 | 1D3623240D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreTextHyperlinkViewAppDelegate.h; sourceTree = ""; }; 55 | 1D3623250D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreTextHyperlinkViewAppDelegate.m; sourceTree = ""; }; 56 | 1D6058910D05DD3D006BFB54 /* CoreTextHyperlinkView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoreTextHyperlinkView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 58 | 288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 59 | 2899E5210DE3E06400AC0155 /* CoreTextHyperlinkViewViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CoreTextHyperlinkViewViewController.xib; sourceTree = ""; }; 60 | 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 61 | 28D7ACF60DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreTextHyperlinkViewViewController.h; sourceTree = ""; }; 62 | 28D7ACF70DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreTextHyperlinkViewViewController.m; sourceTree = ""; }; 63 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 64 | 32CA4F630368D1EE00C91783 /* CoreTextHyperlinkView_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreTextHyperlinkView_Prefix.pch; sourceTree = ""; }; 65 | 53D809CB15801FBC00F4CD19 /* JSEmailCoreTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSEmailCoreTextView.h; sourceTree = ""; }; 66 | 53D809CC15801FBC00F4CD19 /* JSEmailCoreTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSEmailCoreTextView.m; sourceTree = ""; }; 67 | 8D1107310486CEB800E47090 /* CoreTextHyperlinkView-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "CoreTextHyperlinkView-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 76 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 77 | 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */, 78 | 1A7A8B5712C4CA10003283E5 /* CoreText.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 080E96DDFE201D6D7F000001 /* Classes */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 1A7A8B3F12C4C9C6003283E5 /* AutoHyperlinks */, 89 | 1D3623240D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.h */, 90 | 1D3623250D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.m */, 91 | 28D7ACF60DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.h */, 92 | 28D7ACF70DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.m */, 93 | 1A7A8B3312C4C945003283E5 /* JSCoreTextView.h */, 94 | 1A7A8B3412C4C945003283E5 /* JSCoreTextView.m */, 95 | 1A62C43E12D5E6EA00003E1E /* JSTwitterCoreTextView.h */, 96 | 1A62C43F12D5E6EA00003E1E /* JSTwitterCoreTextView.m */, 97 | 53D809CB15801FBC00F4CD19 /* JSEmailCoreTextView.h */, 98 | 53D809CC15801FBC00F4CD19 /* JSEmailCoreTextView.m */, 99 | 1ACBF8131307576E0050711A /* JSWebViewController.h */, 100 | 1ACBF8141307576E0050711A /* JSWebViewController.m */, 101 | 1A4DCD3E16EE796A00271051 /* JSTableViewController.h */, 102 | 1A4DCD3F16EE796A00271051 /* JSTableViewController.m */, 103 | 1A4DCD4116EE798D00271051 /* JSTableViewCell.h */, 104 | 1A4DCD4216EE798D00271051 /* JSTableViewCell.m */, 105 | ); 106 | path = Classes; 107 | sourceTree = ""; 108 | }; 109 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 1D6058910D05DD3D006BFB54 /* CoreTextHyperlinkView.app */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 1A7A8B3F12C4C9C6003283E5 /* AutoHyperlinks */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 1A7A8B4012C4C9C6003283E5 /* AHHyperlinkScanner.h */, 121 | 1A7A8B4112C4C9C6003283E5 /* AHHyperlinkScanner.m */, 122 | 1A7A8B4212C4C9C6003283E5 /* AHLinkLexer.h */, 123 | 1A7A8B4312C4C9C6003283E5 /* AHLinkLexer.l */, 124 | 1A7A8B4412C4C9C6003283E5 /* AHMarkedHyperlink.h */, 125 | 1A7A8B4512C4C9C6003283E5 /* AHMarkedHyperlink.m */, 126 | 1A7A8B4612C4C9C6003283E5 /* AutoHyperlinks.h */, 127 | ); 128 | path = AutoHyperlinks; 129 | sourceTree = ""; 130 | }; 131 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 1A4DCD4416EE80EB00271051 /* Default-568h@2x.png */, 135 | 080E96DDFE201D6D7F000001 /* Classes */, 136 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 137 | 29B97317FDCFA39411CA2CEA /* Resources */, 138 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 139 | 19C28FACFE9D520D11CA2CBB /* Products */, 140 | ); 141 | name = CustomTemplate; 142 | sourceTree = ""; 143 | }; 144 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 32CA4F630368D1EE00C91783 /* CoreTextHyperlinkView_Prefix.pch */, 148 | 29B97316FDCFA39411CA2CEA /* main.m */, 149 | ); 150 | name = "Other Sources"; 151 | sourceTree = ""; 152 | }; 153 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 1ACBF8151307576E0050711A /* JSWebViewController.xib */, 157 | 2899E5210DE3E06400AC0155 /* CoreTextHyperlinkViewViewController.xib */, 158 | 28AD733E0D9D9553002E5188 /* MainWindow.xib */, 159 | 8D1107310486CEB800E47090 /* CoreTextHyperlinkView-Info.plist */, 160 | ); 161 | name = Resources; 162 | sourceTree = ""; 163 | }; 164 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 168 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 169 | 288765A40DF7441C002DB57D /* CoreGraphics.framework */, 170 | 1A7A8B5612C4CA10003283E5 /* CoreText.framework */, 171 | ); 172 | name = Frameworks; 173 | sourceTree = ""; 174 | }; 175 | /* End PBXGroup section */ 176 | 177 | /* Begin PBXNativeTarget section */ 178 | 1D6058900D05DD3D006BFB54 /* CoreTextHyperlinkView */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "CoreTextHyperlinkView" */; 181 | buildPhases = ( 182 | 1D60588D0D05DD3D006BFB54 /* Resources */, 183 | 1D60588E0D05DD3D006BFB54 /* Sources */, 184 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = CoreTextHyperlinkView; 191 | productName = CoreTextHyperlinkView; 192 | productReference = 1D6058910D05DD3D006BFB54 /* CoreTextHyperlinkView.app */; 193 | productType = "com.apple.product-type.application"; 194 | }; 195 | /* End PBXNativeTarget section */ 196 | 197 | /* Begin PBXProject section */ 198 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 199 | isa = PBXProject; 200 | attributes = { 201 | LastUpgradeCheck = 0510; 202 | }; 203 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CoreTextHyperlinkView" */; 204 | compatibilityVersion = "Xcode 3.2"; 205 | developmentRegion = English; 206 | hasScannedForEncodings = 1; 207 | knownRegions = ( 208 | English, 209 | Japanese, 210 | French, 211 | German, 212 | ); 213 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 214 | projectDirPath = ""; 215 | projectRoot = ""; 216 | targets = ( 217 | 1D6058900D05DD3D006BFB54 /* CoreTextHyperlinkView */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXResourcesBuildPhase section */ 223 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, 228 | 2899E5220DE3E06400AC0155 /* CoreTextHyperlinkViewViewController.xib in Resources */, 229 | 1ACBF8171307576E0050711A /* JSWebViewController.xib in Resources */, 230 | 1A4DCD4516EE80EB00271051 /* Default-568h@2x.png in Resources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXResourcesBuildPhase section */ 235 | 236 | /* Begin PBXSourcesBuildPhase section */ 237 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 238 | isa = PBXSourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 242 | 1D3623260D0F684500981E51 /* CoreTextHyperlinkViewAppDelegate.m in Sources */, 243 | 28D7ACF80DDB3853001CB0EB /* CoreTextHyperlinkViewViewController.m in Sources */, 244 | 1A7A8B3512C4C945003283E5 /* JSCoreTextView.m in Sources */, 245 | 1A7A8B4712C4C9C6003283E5 /* AHHyperlinkScanner.m in Sources */, 246 | 1A7A8B4812C4C9C6003283E5 /* AHLinkLexer.l in Sources */, 247 | 1A7A8B4912C4C9C6003283E5 /* AHMarkedHyperlink.m in Sources */, 248 | 1A62C44012D5E6EA00003E1E /* JSTwitterCoreTextView.m in Sources */, 249 | 1ACBF8161307576E0050711A /* JSWebViewController.m in Sources */, 250 | 53D809CD15801FBC00F4CD19 /* JSEmailCoreTextView.m in Sources */, 251 | 1A4DCD4016EE796A00271051 /* JSTableViewController.m in Sources */, 252 | 1A4DCD4316EE798D00271051 /* JSTableViewCell.m in Sources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXSourcesBuildPhase section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | COPY_PHASE_STRIP = NO; 264 | GCC_DYNAMIC_NO_PIC = NO; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 267 | GCC_PREFIX_HEADER = CoreTextHyperlinkView_Prefix.pch; 268 | INFOPLIST_FILE = "CoreTextHyperlinkView-Info.plist"; 269 | IPHONEOS_DEPLOYMENT_TARGET = 4.2; 270 | PRODUCT_NAME = CoreTextHyperlinkView; 271 | TARGETED_DEVICE_FAMILY = "1,2"; 272 | }; 273 | name = Debug; 274 | }; 275 | 1D6058950D05DD3E006BFB54 /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | COPY_PHASE_STRIP = YES; 280 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 281 | GCC_PREFIX_HEADER = CoreTextHyperlinkView_Prefix.pch; 282 | INFOPLIST_FILE = "CoreTextHyperlinkView-Info.plist"; 283 | IPHONEOS_DEPLOYMENT_TARGET = 4.2; 284 | PRODUCT_NAME = CoreTextHyperlinkView; 285 | TARGETED_DEVICE_FAMILY = "1,2"; 286 | VALIDATE_PRODUCT = YES; 287 | }; 288 | name = Release; 289 | }; 290 | C01FCF4F08A954540054247B /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 294 | GCC_C_LANGUAGE_STANDARD = c99; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 296 | GCC_WARN_UNUSED_VARIABLE = YES; 297 | ONLY_ACTIVE_ARCH = YES; 298 | SDKROOT = iphoneos; 299 | }; 300 | name = Debug; 301 | }; 302 | C01FCF5008A954540054247B /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 306 | GCC_C_LANGUAGE_STANDARD = c99; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 310 | SDKROOT = iphoneos; 311 | }; 312 | name = Release; 313 | }; 314 | /* End XCBuildConfiguration section */ 315 | 316 | /* Begin XCConfigurationList section */ 317 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "CoreTextHyperlinkView" */ = { 318 | isa = XCConfigurationList; 319 | buildConfigurations = ( 320 | 1D6058940D05DD3E006BFB54 /* Debug */, 321 | 1D6058950D05DD3E006BFB54 /* Release */, 322 | ); 323 | defaultConfigurationIsVisible = 0; 324 | defaultConfigurationName = Release; 325 | }; 326 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CoreTextHyperlinkView" */ = { 327 | isa = XCConfigurationList; 328 | buildConfigurations = ( 329 | C01FCF4F08A954540054247B /* Debug */, 330 | C01FCF5008A954540054247B /* Release */, 331 | ); 332 | defaultConfigurationIsVisible = 0; 333 | defaultConfigurationName = Release; 334 | }; 335 | /* End XCConfigurationList section */ 336 | }; 337 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 338 | } 339 | -------------------------------------------------------------------------------- /CoreTextHyperlinkViewViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1056 5 | 10J567 6 | 1294 7 | 1038.35 8 | 462.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 294 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIView 17 | 18 | 19 | YES 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | YES 24 | 25 | YES 26 | 27 | 28 | 29 | 30 | YES 31 | 32 | IBFilesOwner 33 | IBCocoaTouchFramework 34 | 35 | 36 | IBFirstResponder 37 | IBCocoaTouchFramework 38 | 39 | 40 | 41 | 274 42 | {{0, 20}, {320, 460}} 43 | 44 | 45 | 46 | 3 47 | MC43NQA 48 | 49 | 2 50 | 51 | 52 | NO 53 | 54 | IBCocoaTouchFramework 55 | 56 | 57 | 58 | 59 | YES 60 | 61 | 62 | view 63 | 64 | 65 | 66 | 7 67 | 68 | 69 | 70 | 71 | YES 72 | 73 | 0 74 | 75 | 76 | 77 | 78 | 79 | -1 80 | 81 | 82 | File's Owner 83 | 84 | 85 | -2 86 | 87 | 88 | 89 | 90 | 6 91 | 92 | 93 | 94 | 95 | 96 | 97 | YES 98 | 99 | YES 100 | -1.CustomClassName 101 | -2.CustomClassName 102 | 6.IBEditorWindowLastContentRect 103 | 6.IBPluginDependency 104 | 105 | 106 | YES 107 | CoreTextHyperlinkViewViewController 108 | UIResponder 109 | {{239, 654}, {320, 480}} 110 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 111 | 112 | 113 | 114 | YES 115 | 116 | 117 | 118 | 119 | 120 | YES 121 | 122 | 123 | 124 | 125 | 7 126 | 127 | 128 | 129 | YES 130 | 131 | CoreTextHyperlinkViewViewController 132 | UIViewController 133 | 134 | IBProjectSource 135 | ./Classes/CoreTextHyperlinkViewViewController.h 136 | 137 | 138 | 139 | 140 | 0 141 | IBCocoaTouchFramework 142 | 143 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 144 | 145 | 146 | YES 147 | 3 148 | 294 149 | 150 | 151 | -------------------------------------------------------------------------------- /CoreTextHyperlinkView_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // Prefix header for all source files of the 'CoreTextHyperlinkView' target in the 'CoreTextHyperlinkView' project 30 | // 31 | 32 | #ifdef __OBJC__ 33 | #import 34 | #import 35 | #endif 36 | -------------------------------------------------------------------------------- /Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasarien/CoreTextHyperlinkView/054c7fb3aee1217951cbb2932ada2cbc5361fc37/Default-568h@2x.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 James Addyman (JamSoft). All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are 4 | permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of 7 | conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | of conditions and the following disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 16 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | The views and conclusions contained in the software and documentation are those of the 24 | authors and should not be interpreted as representing official policies, either expressed 25 | or implied, of James Addyman (JamSoft). 26 | -------------------------------------------------------------------------------- /MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1552 5 | 12C3006 6 | 3084 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 2083 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIButton 17 | IBUICustomObject 18 | IBUINavigationBar 19 | IBUINavigationController 20 | IBUINavigationItem 21 | IBUISegmentedControl 22 | IBUIView 23 | IBUIViewController 24 | IBUIWindow 25 | 26 | 27 | YES 28 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 29 | 30 | 31 | PluginDependencyRecalculationVersion 32 | 33 | 34 | 35 | YES 36 | 37 | IBFilesOwner 38 | IBCocoaTouchFramework 39 | 40 | 41 | IBFirstResponder 42 | IBCocoaTouchFramework 43 | 44 | 45 | IBCocoaTouchFramework 46 | 47 | 48 | 49 | 256 50 | {320, 480} 51 | 52 | 53 | 54 | 1 55 | MSAxIDEAA 56 | 57 | NO 58 | NO 59 | 60 | IBCocoaTouchFramework 61 | YES 62 | 63 | 64 | 65 | Item 66 | IBCocoaTouchFramework 67 | 68 | 69 | 70 | 1 71 | 1 72 | 73 | IBCocoaTouchFramework 74 | NO 75 | 76 | 77 | 78 | 79 | 80 | 290 81 | {{25, 7}, {270, 30}} 82 | NO 83 | IBCocoaTouchFramework 84 | 2 85 | 6 86 | 0 87 | 88 | YES 89 | 12 90 | 14 91 | 16 92 | 18 93 | 20 94 | 22 95 | 96 | 97 | YES 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | YES 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | YES 116 | {0, 0} 117 | {0, 0} 118 | {0, 0} 119 | {0, 0} 120 | {0, 0} 121 | {0, 0} 122 | 123 | 124 | YES 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | IBCocoaTouchFramework 134 | 135 | CoreTextHyperlinkViewViewController 136 | 137 | 138 | 1 139 | 1 140 | 141 | IBCocoaTouchFramework 142 | NO 143 | 144 | 145 | 146 | 147 | 1 148 | 1 149 | 150 | IBCocoaTouchFramework 151 | NO 152 | 153 | 154 | 256 155 | {0, 0} 156 | NO 157 | YES 158 | YES 159 | IBCocoaTouchFramework 160 | 161 | 162 | YES 163 | 164 | 165 | 166 | 274 167 | 168 | YES 169 | 170 | 171 | 292 172 | {{80, 136}, {160, 44}} 173 | 174 | 175 | _NS:9 176 | NO 177 | IBCocoaTouchFramework 178 | 0 179 | 0 180 | 1 181 | example 1 182 | 183 | 3 184 | MQA 185 | 186 | 187 | 1 188 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 189 | 190 | 191 | 3 192 | MC41AA 193 | 194 | 195 | 2 196 | 15 197 | 198 | 199 | Helvetica-Bold 200 | 15 201 | 16 202 | 203 | 204 | 205 | 206 | 292 207 | {{80, 237}, {160, 44}} 208 | 209 | 210 | _NS:9 211 | NO 212 | IBCocoaTouchFramework 213 | 0 214 | 0 215 | 1 216 | table view example 217 | 218 | 219 | 1 220 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 221 | 222 | 223 | 224 | 225 | 226 | 227 | {{0, 64}, {320, 416}} 228 | 229 | 230 | _NS:9 231 | 232 | 3 233 | MQA 234 | 235 | 2 236 | 237 | 238 | IBCocoaTouchFramework 239 | 240 | 241 | Item 242 | IBCocoaTouchFramework 243 | 244 | 245 | 246 | 247 | 1 248 | 1 249 | 250 | IBCocoaTouchFramework 251 | NO 252 | 253 | 254 | 255 | 256 | 257 | 258 | YES 259 | 260 | 261 | delegate 262 | 263 | 264 | 265 | 4 266 | 267 | 268 | 269 | window 270 | 271 | 272 | 273 | 14 274 | 275 | 276 | 277 | navController 278 | 279 | 280 | 281 | 22 282 | 283 | 284 | 285 | tableViewController 286 | 287 | 288 | 289 | 37 290 | 291 | 292 | 293 | viewController 294 | 295 | 296 | 297 | 38 298 | 299 | 300 | 301 | example1: 302 | 303 | 304 | 7 305 | 306 | 35 307 | 308 | 309 | 310 | example2: 311 | 312 | 313 | 7 314 | 315 | 36 316 | 317 | 318 | 319 | 320 | YES 321 | 322 | 0 323 | 324 | YES 325 | 326 | 327 | 328 | 329 | 330 | -1 331 | 332 | 333 | File's Owner 334 | 335 | 336 | 3 337 | 338 | 339 | CoreTextHyperlinkView App Delegate 340 | 341 | 342 | -2 343 | 344 | 345 | 346 | 347 | 12 348 | 349 | 350 | 351 | 352 | 16 353 | 354 | 355 | YES 356 | 357 | 358 | 359 | 360 | 361 | 362 | 18 363 | 364 | 365 | 366 | 367 | 10 368 | 369 | 370 | YES 371 | 372 | 373 | 374 | 375 | 376 | 20 377 | 378 | 379 | YES 380 | 381 | 382 | 383 | 384 | 385 | 21 386 | 387 | 388 | 389 | 390 | 23 391 | 392 | 393 | YES 394 | 395 | 396 | 397 | 398 | 399 | 24 400 | 401 | 402 | 403 | 404 | 30 405 | 406 | 407 | YES 408 | 409 | 410 | 411 | 412 | 413 | 414 | 31 415 | 416 | 417 | 418 | 419 | 32 420 | 421 | 422 | YES 423 | 424 | 425 | 426 | 427 | 428 | 429 | 33 430 | 431 | 432 | 433 | 434 | 34 435 | 436 | 437 | 438 | 439 | 440 | 441 | YES 442 | 443 | YES 444 | -1.CustomClassName 445 | -1.IBPluginDependency 446 | -2.CustomClassName 447 | -2.IBPluginDependency 448 | 10.CustomClassName 449 | 10.IBPluginDependency 450 | 12.IBPluginDependency 451 | 16.IBPluginDependency 452 | 18.IBPluginDependency 453 | 20.IBPluginDependency 454 | 21.IBPluginDependency 455 | 23.CustomClassName 456 | 23.IBPluginDependency 457 | 24.IBPluginDependency 458 | 3.CustomClassName 459 | 3.IBPluginDependency 460 | 30.IBPluginDependency 461 | 31.IBPluginDependency 462 | 32.IBPluginDependency 463 | 33.IBPluginDependency 464 | 34.IBPluginDependency 465 | 466 | 467 | YES 468 | UIApplication 469 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 470 | UIResponder 471 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 472 | CoreTextHyperlinkViewViewController 473 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 474 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 475 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 476 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 477 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 478 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 479 | JSTableViewController 480 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 481 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 482 | CoreTextHyperlinkViewAppDelegate 483 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 484 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 485 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 486 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 487 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 488 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 489 | 490 | 491 | 492 | YES 493 | 494 | 495 | 496 | 497 | 498 | YES 499 | 500 | 501 | 502 | 503 | 38 504 | 505 | 506 | 0 507 | IBCocoaTouchFramework 508 | 509 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 510 | 511 | 512 | YES 513 | 3 514 | 2083 515 | 516 | 517 | -------------------------------------------------------------------------------- /README.mdown: -------------------------------------------------------------------------------- 1 | Core Text Hyperlink View 2 | ======================== 3 | 4 | - Uses AutoHyperlinks code from Adium [http://code.google.com/p/maccode/wiki/AutoHyperlinks](http://code.google.com/p/maccode/wiki/AutoHyperlinks) 5 | 6 | Project includes an example of how to use the core text view. It's pretty simple - create the view with a 0 height frame, set the attributes, set its text, and then ask it for it's size so that the height is just correct. It will calculate based on the constraining width, and any padding that you set. 7 | 8 | There is also a Twitter specific subclass that enables @usernames and #hashtags to be linkified. 9 | 10 | **Settable Attributes:** 11 | 12 | - textAlignment 13 | - textColor 14 | - linkColor 15 | - highlightedLinkColor 16 | - fontName 17 | - fontSize 18 | - paddingTop 19 | - paddingLeft 20 | - backgroundImage 21 | - bgImageTopStretchCap 22 | - bgImageLeftStretchCap 23 | 24 | To use CoreTextHyperlinkView in your apps: 25 | 26 | - Copy The contents of the AutoHyperlinks folder, and the files JSCoreTextView.h & .m and, if you want Twitter username and hashtag support, copy JSTwitterCoreTextView.h & .m too. 27 | - Link against the CoreText framework. 28 | 29 | Screenshot 30 | ========== 31 | 32 | ![Core Text Hyperlink View](http://d.pr/i/sETn+) 33 | 34 | 35 | Licenses 36 | ======== 37 | 38 | CoreTextHyperlinkView (Simplified BSD License) 39 | --------------------- 40 | 41 | Copyright 2011 James Addyman (JamSoft). All rights reserved. 42 | 43 | Redistribution and use in source and binary forms, with or without modification, are 44 | permitted provided that the following conditions are met: 45 | 46 | 1. Redistributions of source code must retain the above copyright notice, this list of 47 | conditions and the following disclaimer. 48 | 49 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 50 | of conditions and the following disclaimer in the documentation and/or other materials 51 | provided with the distribution. 52 | 53 | THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 54 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 55 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 56 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 58 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 59 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 60 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 | 63 | The views and conclusions contained in the software and documentation are those of the 64 | authors and should not be interpreted as representing official policies, either expressed 65 | or implied, of James Addyman (JamSoft). 66 | 67 | AutoHyperlinks (Modified BSD License) 68 | -------------- 69 | 70 | 71 | The AutoHyperlinks Framework is the legal property of its developers (DEVELOPERS), whose names are listed in the 72 | copyright file included with this source distribution. 73 | 74 | Redistribution and use in source and binary forms, with or without 75 | modification, are permitted provided that the following conditions are met: 76 | 77 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 78 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 79 | - Neither the name of the AutoHyperlinks Framework nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 80 | 81 | THIS SOFTWARE IS PROVIDED BY ITS DEVELOPERS ``AS IS'' AND ANY 82 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 83 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 84 | DISCLAIMED. IN NO EVENT SHALL ITS DEVELOPERS BE LIABLE FOR ANY 85 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 86 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 87 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 88 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 89 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 90 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 91 | 92 | AutoHyperlinks Framework 93 | (c) 2004-2008 by the following: 94 | 95 | Colin Barrett 96 | Graham Booker 97 | Jorge Salvador Caffarena 98 | Evan Schoenberg 99 | Augie Fackler 100 | Stephen Holt 101 | Peter Hosey 102 | Adam Iser 103 | Jeffrey Melloy 104 | Toby Peterson 105 | Eric Richie 106 | David Smith 107 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2011 James Addyman (JamSoft). All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, are 5 | // permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of 8 | // conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | // of conditions and the following disclaimer in the documentation and/or other materials 12 | // provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY JAMES ADDYMAN (JAMSOFT) ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES ADDYMAN (JAMSOFT) OR 17 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are those of the 25 | // authors and should not be interpreted as representing official policies, either expressed 26 | // or implied, of James Addyman (JamSoft). 27 | // 28 | // 29 | // main.m 30 | // CoreTextHyperlinkView 31 | // 32 | // Created by James Addyman on 24/12/2011. 33 | // Copyright 2011 JamSoft. All rights reserved. 34 | // 35 | 36 | #import 37 | 38 | int main(int argc, char *argv[]) { 39 | 40 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 41 | int retVal = UIApplicationMain(argc, argv, nil, nil); 42 | [pool release]; 43 | return retVal; 44 | } 45 | --------------------------------------------------------------------------------