├── FuckingGreatAdvice ├── en.lproj │ └── InfoPlist.strings ├── thumbnail.png ├── thumbnail@2x.png ├── FuckingGreatAdvice-Prefix.pch ├── FuckingGreatAdviceView.h ├── FuckingGreatAdvice-Info.plist ├── GTMNSString+HTML.h ├── FuckingGreatAdviceView.m └── GTMNSString+HTML.m ├── Release ├── .DS_Store ├── screenshot.png └── FuckingGreatAdvice.saver.zip ├── FuckingGreatAdvice.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── DeveloperExcuses.xccheckout └── project.pbxproj ├── README.md ├── LICENSE └── .gitignore /FuckingGreatAdvice/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Release/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anodamobi/FuckingGreatAdvice/HEAD/Release/.DS_Store -------------------------------------------------------------------------------- /Release/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anodamobi/FuckingGreatAdvice/HEAD/Release/screenshot.png -------------------------------------------------------------------------------- /FuckingGreatAdvice/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anodamobi/FuckingGreatAdvice/HEAD/FuckingGreatAdvice/thumbnail.png -------------------------------------------------------------------------------- /FuckingGreatAdvice/thumbnail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anodamobi/FuckingGreatAdvice/HEAD/FuckingGreatAdvice/thumbnail@2x.png -------------------------------------------------------------------------------- /Release/FuckingGreatAdvice.saver.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anodamobi/FuckingGreatAdvice/HEAD/Release/FuckingGreatAdvice.saver.zip -------------------------------------------------------------------------------- /FuckingGreatAdvice/FuckingGreatAdvice-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'DeveloperExcuses' target in the 'DeveloperExcuses' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /FuckingGreatAdvice.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FuckingGreatAdvice/FuckingGreatAdviceView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FuckingGreatAdviceView.h 3 | // FuckingGreatAdvice 4 | // 5 | // Created by Alex Zavrazhniy on 07.10.16. 6 | // Copyright (c) 2016 ANODA. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FuckingGreatAdviceView : ScreenSaverView 12 | 13 | @property (strong) NSTextField *label; 14 | @property (assign) BOOL shouldFetchQuote; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FuckingGreatAdvice 2 | OSX ScreenSaver with quotes from fucking-great-advice.ru 3 | 4 | ![Screenshot](Release/screenshot.png) 5 | 6 | Dead simple Screensaver which periodically shows a quote from [http://fucking-great-advice.ru](http://fucking-great-advice.ru), that's it. 7 | 8 | Not officially affiliated with [http://fucking-great-advice.ru](http://fucking-great-advice.ru) 9 | 10 | Grab the current [Release here](https://github.com/anodamobi/FuckingGreatAdvice/raw/master/Release/FuckingGreatAdvice.saver.zip)! 11 | 12 | Inspired from [https://github.com/kimar/DeveloperExcuses](kimar's DeveloperExcuses) 13 | 14 | And feel free to fork and contribute ;-) 15 | 16 | # TODO 17 | 18 | - [ ] Add Settings popup 19 | - [ ] Add Category customisation dropdown 20 | - [ ] Add some visual options (like dark mode, etc) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 ANODA Mobile Development Agency 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /FuckingGreatAdvice/FuckingGreatAdvice-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | thumbnail.png 11 | CFBundleIdentifier 12 | mobi.anoda.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.1 25 | NSHumanReadableCopyright 26 | Copyright © 2016 ANODA. All rights reserved. 27 | NSPrincipalClass 28 | DeveloperExcusesView 29 | NSSupportsSuddenTermination 30 | 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /FuckingGreatAdvice.xcodeproj/project.xcworkspace/xcshareddata/DeveloperExcuses.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 4CC4EB4B-918E-4C47-A3F7-8C18775BABD2 9 | IDESourceControlProjectName 10 | DeveloperExcuses 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 283B2FC7-2770-4804-852B-9FAAAD9A31FD 14 | ssh://github.com/kimar/DeveloperExcuses.git 15 | 16 | IDESourceControlProjectPath 17 | DeveloperExcuses.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 283B2FC7-2770-4804-852B-9FAAAD9A31FD 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/kimar/DeveloperExcuses.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 283B2FC7-2770-4804-852B-9FAAAD9A31FD 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 283B2FC7-2770-4804-852B-9FAAAD9A31FD 36 | IDESourceControlWCCName 37 | DeveloperExcuses 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /FuckingGreatAdvice/GTMNSString+HTML.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMNSString+HTML.h 3 | // Dealing with NSStrings that contain HTML 4 | // 5 | // Copyright 2006-2008 Google Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | // use this file except in compliance with the License. You may obtain a copy 9 | // of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | // License for the specific language governing permissions and limitations under 17 | // the License. 18 | // 19 | 20 | #import 21 | 22 | /// Utilities for NSStrings containing HTML 23 | @interface NSString (GTMNSStringHTMLAdditions) 24 | 25 | /// Get a string where internal characters that need escaping for HTML are escaped 26 | // 27 | /// For example, '&' become '&'. This will only cover characters from table 28 | /// A.2.2 of http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 29 | /// which is what you want for a unicode encoded webpage. If you have a ascii 30 | /// or non-encoded webpage, please use stringByEscapingAsciiHTML which will 31 | /// encode all characters. 32 | /// 33 | /// For obvious reasons this call is only safe once. 34 | // 35 | // Returns: 36 | // Autoreleased NSString 37 | // 38 | - (NSString *)gtm_stringByEscapingForHTML; 39 | 40 | /// Get a string where internal characters that need escaping for HTML are escaped 41 | // 42 | /// For example, '&' become '&' 43 | /// All non-mapped characters (unicode that don't have a &keyword; mapping) 44 | /// will be converted to the appropriate &#xxx; value. If your webpage is 45 | /// unicode encoded (UTF16 or UTF8) use stringByEscapingHTML instead as it is 46 | /// faster, and produces less bloated and more readable HTML (as long as you 47 | /// are using a unicode compliant HTML reader). 48 | /// 49 | /// For obvious reasons this call is only safe once. 50 | // 51 | // Returns: 52 | // Autoreleased NSString 53 | // 54 | - (NSString *)gtm_stringByEscapingForAsciiHTML; 55 | 56 | /// Get a string where internal characters that are escaped for HTML are unescaped 57 | // 58 | /// For example, '&' becomes '&' 59 | /// Handles and 2 cases as well 60 | /// 61 | // Returns: 62 | // Autoreleased NSString 63 | // 64 | - (NSString *)gtm_stringByUnescapingFromHTML; 65 | 66 | @end -------------------------------------------------------------------------------- /FuckingGreatAdvice/FuckingGreatAdviceView.m: -------------------------------------------------------------------------------- 1 | // 2 | // FuckingGreatAdviceView.m 3 | // FuckingGreatAdvice 4 | // 5 | // Created by Alex Zavrazhniy on 07.10.16. 6 | // Copyright (c) 2013 ANODA. All rights reserved. 7 | // 8 | 9 | #import "FuckingGreatAdviceView.h" 10 | #import "GTMNSString+HTML.h" 11 | 12 | @interface FuckingGreatAdviceView() 13 | 14 | @property NSUserDefaults *defaults; 15 | 16 | @end 17 | 18 | NSString *kLastFetchedQuote = @"kLastFetchedQuote"; 19 | 20 | @implementation FuckingGreatAdviceView 21 | 22 | - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview 23 | { 24 | self = [super initWithFrame:frame isPreview:isPreview]; 25 | if (self) 26 | { 27 | [self initialize]; 28 | 29 | } 30 | return self; 31 | } 32 | 33 | - (id)initWithCoder:(NSCoder *)aDecoder 34 | { 35 | self = [super initWithCoder:aDecoder]; 36 | if (self) 37 | { 38 | [self initialize]; 39 | } 40 | 41 | return self; 42 | } 43 | 44 | - (void)drawRect:(NSRect)rect 45 | { 46 | [super drawRect:rect]; 47 | 48 | CGRect newFrame = self.label.frame; 49 | CGFloat height = [_label.stringValue sizeWithAttributes:@{NSFontAttributeName: _label.font}].height; 50 | newFrame.size.height = height; 51 | newFrame.origin.y = (NSHeight(self.bounds) - height) / 2; 52 | _label.frame = newFrame; 53 | 54 | [[NSColor whiteColor] setFill]; 55 | NSRectFill(rect); 56 | } 57 | 58 | - (void)animateOneFrame 59 | { 60 | [self fetchNextQuote]; 61 | } 62 | 63 | - (BOOL)hasConfigureSheet 64 | { 65 | return NO; 66 | } 67 | 68 | - (NSWindow*)configureSheet { 69 | return nil; 70 | } 71 | 72 | - (void) initialize { 73 | [self setAnimationTimeInterval:0.5]; 74 | _defaults = [NSUserDefaults standardUserDefaults]; 75 | [_defaults setValue:nil forKey:kLastFetchedQuote]; 76 | [_defaults synchronize]; 77 | 78 | [self configureLabel]; 79 | [self restoreLastQuote]; 80 | [self fetchNextQuote]; 81 | } 82 | 83 | - (void)configureLabel { 84 | _label = [[NSTextField alloc] initWithFrame:self.bounds]; 85 | _label.autoresizingMask = NSViewWidthSizable; 86 | _label.alignment = NSCenterTextAlignment; 87 | 88 | _label.stringValue = @"Ща, сек..."; 89 | _label.textColor = [NSColor blackColor]; 90 | _label.font = [NSFont fontWithName:@"Helvetica Bold" size:(self.preview ? 24.0 : 80.0)]; 91 | 92 | _label.backgroundColor = [NSColor clearColor]; 93 | [_label setEditable:NO]; 94 | [_label setBezeled:NO]; 95 | 96 | [self addSubview:_label]; 97 | } 98 | 99 | - (void)restoreLastQuote { 100 | 101 | self.shouldFetchQuote = YES; 102 | NSString *lastQuote = [_defaults valueForKey:kLastFetchedQuote]; 103 | [self setQuote: lastQuote]; 104 | } 105 | 106 | - (void)scheduleNextFetch { 107 | double delayInSeconds = 10.0; 108 | dispatch_time_t fireTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 109 | dispatch_after(fireTime, dispatch_get_main_queue(), ^(void){ 110 | self.shouldFetchQuote = YES; 111 | }); 112 | } 113 | 114 | - (void)setQuote:(NSString *) quote { 115 | if (quote != nil) { 116 | _label.stringValue = quote; 117 | [_defaults setObject:quote forKey:kLastFetchedQuote]; 118 | [_defaults synchronize]; 119 | self.shouldFetchQuote = NO; 120 | [self setNeedsDisplay:YES]; 121 | } 122 | 123 | [self scheduleNextFetch]; 124 | } 125 | 126 | 127 | - (void) fetchNextQuote { 128 | @synchronized (self) { 129 | if (!self.shouldFetchQuote) { 130 | return; 131 | } 132 | } 133 | 134 | self.shouldFetchQuote = NO; 135 | 136 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 137 | 138 | NSError *error; 139 | NSString *quote = @"Работай!"; 140 | 141 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://fucking-great-advice.ru/api/random_by_tag/%D0%BA%D0%BE%D0%B4%D0%B5%D1%80%D1%83"]]; 142 | if (data) 143 | { 144 | NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 145 | quote = dictionary[@"text"]; 146 | quote = [quote stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; 147 | quote = [quote gtm_stringByUnescapingFromHTML]; 148 | quote = [NSString stringWithFormat:@"— %@", quote]; 149 | } 150 | 151 | dispatch_async(dispatch_get_main_queue(), ^{ 152 | [self scheduleNextFetch]; 153 | [self setQuote: quote]; 154 | }); 155 | }); 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /FuckingGreatAdvice.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2A9D0E6817DB87B300DBFFBD /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A9D0E6717DB87B300DBFFBD /* Cocoa.framework */; }; 11 | 2A9D0E6A17DB87B300DBFFBD /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A9D0E6917DB87B300DBFFBD /* ScreenSaver.framework */; }; 12 | 2A9D0E7417DB87B300DBFFBD /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2A9D0E7217DB87B300DBFFBD /* InfoPlist.strings */; }; 13 | DA75544B1DAA9651009BA6F5 /* FuckingGreatAdviceView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7554471DAA9651009BA6F5 /* FuckingGreatAdviceView.h */; }; 14 | DA75544C1DAA9651009BA6F5 /* FuckingGreatAdviceView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA7554481DAA9651009BA6F5 /* FuckingGreatAdviceView.m */; }; 15 | DA75544D1DAA9651009BA6F5 /* GTMNSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7554491DAA9651009BA6F5 /* GTMNSString+HTML.h */; }; 16 | DA75544E1DAA9651009BA6F5 /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = DA75544A1DAA9651009BA6F5 /* GTMNSString+HTML.m */; }; 17 | DA7554531DAA9660009BA6F5 /* FuckingGreatAdvice-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA75544F1DAA9660009BA6F5 /* FuckingGreatAdvice-Info.plist */; }; 18 | DA7554541DAA9660009BA6F5 /* FuckingGreatAdvice-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = DA7554501DAA9660009BA6F5 /* FuckingGreatAdvice-Prefix.pch */; }; 19 | DA7554551DAA9660009BA6F5 /* thumbnail.png in Resources */ = {isa = PBXBuildFile; fileRef = DA7554511DAA9660009BA6F5 /* thumbnail.png */; }; 20 | DA7554561DAA9660009BA6F5 /* thumbnail@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA7554521DAA9660009BA6F5 /* thumbnail@2x.png */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 2A9D0E6417DB87B300DBFFBD /* FuckingGreatAdvice.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FuckingGreatAdvice.saver; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 2A9D0E6717DB87B300DBFFBD /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 26 | 2A9D0E6917DB87B300DBFFBD /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = System/Library/Frameworks/ScreenSaver.framework; sourceTree = SDKROOT; }; 27 | 2A9D0E6C17DB87B300DBFFBD /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 28 | 2A9D0E6D17DB87B300DBFFBD /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 29 | 2A9D0E6E17DB87B300DBFFBD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | 2A9D0E7317DB87B300DBFFBD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 31 | DA7554471DAA9651009BA6F5 /* FuckingGreatAdviceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FuckingGreatAdviceView.h; path = FuckingGreatAdvice/FuckingGreatAdviceView.h; sourceTree = SOURCE_ROOT; }; 32 | DA7554481DAA9651009BA6F5 /* FuckingGreatAdviceView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FuckingGreatAdviceView.m; path = FuckingGreatAdvice/FuckingGreatAdviceView.m; sourceTree = SOURCE_ROOT; }; 33 | DA7554491DAA9651009BA6F5 /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "GTMNSString+HTML.h"; path = "FuckingGreatAdvice/GTMNSString+HTML.h"; sourceTree = SOURCE_ROOT; }; 34 | DA75544A1DAA9651009BA6F5 /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "GTMNSString+HTML.m"; path = "FuckingGreatAdvice/GTMNSString+HTML.m"; sourceTree = SOURCE_ROOT; }; 35 | DA75544F1DAA9660009BA6F5 /* FuckingGreatAdvice-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "FuckingGreatAdvice-Info.plist"; path = "FuckingGreatAdvice/FuckingGreatAdvice-Info.plist"; sourceTree = SOURCE_ROOT; }; 36 | DA7554501DAA9660009BA6F5 /* FuckingGreatAdvice-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FuckingGreatAdvice-Prefix.pch"; path = "FuckingGreatAdvice/FuckingGreatAdvice-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 37 | DA7554511DAA9660009BA6F5 /* thumbnail.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = thumbnail.png; path = FuckingGreatAdvice/thumbnail.png; sourceTree = SOURCE_ROOT; }; 38 | DA7554521DAA9660009BA6F5 /* thumbnail@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "thumbnail@2x.png"; path = "FuckingGreatAdvice/thumbnail@2x.png"; sourceTree = SOURCE_ROOT; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 2A9D0E5F17DB87B300DBFFBD /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 2A9D0E6817DB87B300DBFFBD /* Cocoa.framework in Frameworks */, 47 | 2A9D0E6A17DB87B300DBFFBD /* ScreenSaver.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 2A9D0E5917DB87B300DBFFBD = { 55 | isa = PBXGroup; 56 | children = ( 57 | 2A9D0E6F17DB87B300DBFFBD /* FuckingGreatAdvice */, 58 | 2A9D0E6617DB87B300DBFFBD /* Frameworks */, 59 | 2A9D0E6517DB87B300DBFFBD /* Products */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 2A9D0E6517DB87B300DBFFBD /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 2A9D0E6417DB87B300DBFFBD /* FuckingGreatAdvice.saver */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 2A9D0E6617DB87B300DBFFBD /* Frameworks */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 2A9D0E6717DB87B300DBFFBD /* Cocoa.framework */, 75 | 2A9D0E6917DB87B300DBFFBD /* ScreenSaver.framework */, 76 | 2A9D0E6B17DB87B300DBFFBD /* Other Frameworks */, 77 | ); 78 | name = Frameworks; 79 | sourceTree = ""; 80 | }; 81 | 2A9D0E6B17DB87B300DBFFBD /* Other Frameworks */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 2A9D0E6C17DB87B300DBFFBD /* AppKit.framework */, 85 | 2A9D0E6D17DB87B300DBFFBD /* CoreData.framework */, 86 | 2A9D0E6E17DB87B300DBFFBD /* Foundation.framework */, 87 | ); 88 | name = "Other Frameworks"; 89 | sourceTree = ""; 90 | }; 91 | 2A9D0E6F17DB87B300DBFFBD /* FuckingGreatAdvice */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | DA7554471DAA9651009BA6F5 /* FuckingGreatAdviceView.h */, 95 | DA7554481DAA9651009BA6F5 /* FuckingGreatAdviceView.m */, 96 | DA7554491DAA9651009BA6F5 /* GTMNSString+HTML.h */, 97 | DA75544A1DAA9651009BA6F5 /* GTMNSString+HTML.m */, 98 | 2A9D0E7017DB87B300DBFFBD /* Supporting Files */, 99 | ); 100 | name = FuckingGreatAdvice; 101 | path = DeveloperExcuses; 102 | sourceTree = ""; 103 | }; 104 | 2A9D0E7017DB87B300DBFFBD /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | DA75544F1DAA9660009BA6F5 /* FuckingGreatAdvice-Info.plist */, 108 | DA7554501DAA9660009BA6F5 /* FuckingGreatAdvice-Prefix.pch */, 109 | DA7554511DAA9660009BA6F5 /* thumbnail.png */, 110 | DA7554521DAA9660009BA6F5 /* thumbnail@2x.png */, 111 | 2A9D0E7217DB87B300DBFFBD /* InfoPlist.strings */, 112 | ); 113 | name = "Supporting Files"; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXHeadersBuildPhase section */ 119 | 2A9D0E6017DB87B300DBFFBD /* Headers */ = { 120 | isa = PBXHeadersBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | DA7554541DAA9660009BA6F5 /* FuckingGreatAdvice-Prefix.pch in Headers */, 124 | DA75544D1DAA9651009BA6F5 /* GTMNSString+HTML.h in Headers */, 125 | DA75544B1DAA9651009BA6F5 /* FuckingGreatAdviceView.h in Headers */, 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXHeadersBuildPhase section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 2A9D0E6317DB87B300DBFFBD /* FuckingGreatAdvice */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 2A9D0E7B17DB87B300DBFFBD /* Build configuration list for PBXNativeTarget "FuckingGreatAdvice" */; 135 | buildPhases = ( 136 | 2A9D0E5E17DB87B300DBFFBD /* Sources */, 137 | 2A9D0E5F17DB87B300DBFFBD /* Frameworks */, 138 | 2A9D0E6017DB87B300DBFFBD /* Headers */, 139 | 2A9D0E6117DB87B300DBFFBD /* Resources */, 140 | 2A9D0E6217DB87B300DBFFBD /* Rez */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | ); 146 | name = FuckingGreatAdvice; 147 | productName = DeveloperExcuses; 148 | productReference = 2A9D0E6417DB87B300DBFFBD /* FuckingGreatAdvice.saver */; 149 | productType = "com.apple.product-type.bundle"; 150 | }; 151 | /* End PBXNativeTarget section */ 152 | 153 | /* Begin PBXProject section */ 154 | 2A9D0E5A17DB87B300DBFFBD /* Project object */ = { 155 | isa = PBXProject; 156 | attributes = { 157 | LastUpgradeCheck = 0500; 158 | ORGANIZATIONNAME = "Marcus Kida"; 159 | }; 160 | buildConfigurationList = 2A9D0E5D17DB87B300DBFFBD /* Build configuration list for PBXProject "FuckingGreatAdvice" */; 161 | compatibilityVersion = "Xcode 3.2"; 162 | developmentRegion = English; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | ); 167 | mainGroup = 2A9D0E5917DB87B300DBFFBD; 168 | productRefGroup = 2A9D0E6517DB87B300DBFFBD /* Products */; 169 | projectDirPath = ""; 170 | projectRoot = ""; 171 | targets = ( 172 | 2A9D0E6317DB87B300DBFFBD /* FuckingGreatAdvice */, 173 | ); 174 | }; 175 | /* End PBXProject section */ 176 | 177 | /* Begin PBXResourcesBuildPhase section */ 178 | 2A9D0E6117DB87B300DBFFBD /* Resources */ = { 179 | isa = PBXResourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | DA7554531DAA9660009BA6F5 /* FuckingGreatAdvice-Info.plist in Resources */, 183 | DA7554561DAA9660009BA6F5 /* thumbnail@2x.png in Resources */, 184 | 2A9D0E7417DB87B300DBFFBD /* InfoPlist.strings in Resources */, 185 | DA7554551DAA9660009BA6F5 /* thumbnail.png in Resources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXResourcesBuildPhase section */ 190 | 191 | /* Begin PBXRezBuildPhase section */ 192 | 2A9D0E6217DB87B300DBFFBD /* Rez */ = { 193 | isa = PBXRezBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXRezBuildPhase section */ 200 | 201 | /* Begin PBXSourcesBuildPhase section */ 202 | 2A9D0E5E17DB87B300DBFFBD /* Sources */ = { 203 | isa = PBXSourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | DA75544C1DAA9651009BA6F5 /* FuckingGreatAdviceView.m in Sources */, 207 | DA75544E1DAA9651009BA6F5 /* GTMNSString+HTML.m in Sources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXSourcesBuildPhase section */ 212 | 213 | /* Begin PBXVariantGroup section */ 214 | 2A9D0E7217DB87B300DBFFBD /* InfoPlist.strings */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 2A9D0E7317DB87B300DBFFBD /* en */, 218 | ); 219 | name = InfoPlist.strings; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 2A9D0E7917DB87B300DBFFBD /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_WARN_CONSTANT_CONVERSION = YES; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INT_CONVERSION = YES; 235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 236 | COMBINE_HIDPI_IMAGES = YES; 237 | COPY_PHASE_STRIP = NO; 238 | GCC_C_LANGUAGE_STANDARD = gnu99; 239 | GCC_DYNAMIC_NO_PIC = NO; 240 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 241 | GCC_ENABLE_OBJC_GC = supported; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PREPROCESSOR_DEFINITIONS = ( 244 | "DEBUG=1", 245 | "$(inherited)", 246 | ); 247 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 248 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 251 | GCC_WARN_UNUSED_VARIABLE = YES; 252 | MACOSX_DEPLOYMENT_TARGET = 10.8; 253 | ONLY_ACTIVE_ARCH = YES; 254 | SDKROOT = macosx; 255 | }; 256 | name = Debug; 257 | }; 258 | 2A9D0E7A17DB87B300DBFFBD /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ALWAYS_SEARCH_USER_PATHS = NO; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INT_CONVERSION = YES; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | COMBINE_HIDPI_IMAGES = YES; 270 | COPY_PHASE_STRIP = YES; 271 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 272 | GCC_C_LANGUAGE_STANDARD = gnu99; 273 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 274 | GCC_ENABLE_OBJC_GC = supported; 275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | MACOSX_DEPLOYMENT_TARGET = 10.8; 280 | SDKROOT = macosx; 281 | }; 282 | name = Release; 283 | }; 284 | 2A9D0E7C17DB87B300DBFFBD /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | CLANG_ENABLE_OBJC_ARC = YES; 288 | COMBINE_HIDPI_IMAGES = YES; 289 | GCC_ENABLE_OBJC_GC = unsupported; 290 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 291 | GCC_PREFIX_HEADER = "DeveloperExcuses/FuckingGreatAdvice-Prefix.pch"; 292 | INFOPLIST_FILE = "DeveloperExcuses/FuckingGreatAdvice-Info.plist"; 293 | INSTALL_PATH = "$(HOME)/Library/Screen Savers"; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | WRAPPER_EXTENSION = saver; 296 | }; 297 | name = Debug; 298 | }; 299 | 2A9D0E7D17DB87B300DBFFBD /* Release */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | CLANG_ENABLE_OBJC_ARC = YES; 303 | COMBINE_HIDPI_IMAGES = YES; 304 | GCC_ENABLE_OBJC_GC = unsupported; 305 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 306 | GCC_PREFIX_HEADER = "DeveloperExcuses/FuckingGreatAdvice-Prefix.pch"; 307 | INFOPLIST_FILE = "DeveloperExcuses/FuckingGreatAdvice-Info.plist"; 308 | INSTALL_PATH = "$(HOME)/Library/Screen Savers"; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | WRAPPER_EXTENSION = saver; 311 | }; 312 | name = Release; 313 | }; 314 | /* End XCBuildConfiguration section */ 315 | 316 | /* Begin XCConfigurationList section */ 317 | 2A9D0E5D17DB87B300DBFFBD /* Build configuration list for PBXProject "FuckingGreatAdvice" */ = { 318 | isa = XCConfigurationList; 319 | buildConfigurations = ( 320 | 2A9D0E7917DB87B300DBFFBD /* Debug */, 321 | 2A9D0E7A17DB87B300DBFFBD /* Release */, 322 | ); 323 | defaultConfigurationIsVisible = 0; 324 | defaultConfigurationName = Release; 325 | }; 326 | 2A9D0E7B17DB87B300DBFFBD /* Build configuration list for PBXNativeTarget "FuckingGreatAdvice" */ = { 327 | isa = XCConfigurationList; 328 | buildConfigurations = ( 329 | 2A9D0E7C17DB87B300DBFFBD /* Debug */, 330 | 2A9D0E7D17DB87B300DBFFBD /* Release */, 331 | ); 332 | defaultConfigurationIsVisible = 0; 333 | defaultConfigurationName = Release; 334 | }; 335 | /* End XCConfigurationList section */ 336 | }; 337 | rootObject = 2A9D0E5A17DB87B300DBFFBD /* Project object */; 338 | } 339 | -------------------------------------------------------------------------------- /FuckingGreatAdvice/GTMNSString+HTML.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMNSString+HTML.m 3 | // Dealing with NSStrings that contain HTML 4 | // 5 | // Copyright 2006-2008 Google Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | // use this file except in compliance with the License. You may obtain a copy 9 | // of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | // License for the specific language governing permissions and limitations under 17 | // the License. 18 | // 19 | 20 | //#import "GTMDefines.h" 21 | #import "GTMNSString+HTML.h" 22 | 23 | typedef struct { 24 | __unsafe_unretained NSString *escapeSequence; 25 | unichar uchar; 26 | } HTMLEscapeMap; 27 | 28 | // Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 29 | // Ordered by uchar lowest to highest for bsearching 30 | static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { 31 | // A.2.2. Special characters 32 | { @""", 34 }, 33 | { @"&", 38 }, 34 | { @"'", 39 }, 35 | { @"<", 60 }, 36 | { @">", 62 }, 37 | 38 | // A.2.1. Latin-1 characters 39 | { @" ", 160 }, 40 | { @"¡", 161 }, 41 | { @"¢", 162 }, 42 | { @"£", 163 }, 43 | { @"¤", 164 }, 44 | { @"¥", 165 }, 45 | { @"¦", 166 }, 46 | { @"§", 167 }, 47 | { @"¨", 168 }, 48 | { @"©", 169 }, 49 | { @"ª", 170 }, 50 | { @"«", 171 }, 51 | { @"¬", 172 }, 52 | { @"­", 173 }, 53 | { @"®", 174 }, 54 | { @"¯", 175 }, 55 | { @"°", 176 }, 56 | { @"±", 177 }, 57 | { @"²", 178 }, 58 | { @"³", 179 }, 59 | { @"´", 180 }, 60 | { @"µ", 181 }, 61 | { @"¶", 182 }, 62 | { @"·", 183 }, 63 | { @"¸", 184 }, 64 | { @"¹", 185 }, 65 | { @"º", 186 }, 66 | { @"»", 187 }, 67 | { @"¼", 188 }, 68 | { @"½", 189 }, 69 | { @"¾", 190 }, 70 | { @"¿", 191 }, 71 | { @"À", 192 }, 72 | { @"Á", 193 }, 73 | { @"Â", 194 }, 74 | { @"Ã", 195 }, 75 | { @"Ä", 196 }, 76 | { @"Å", 197 }, 77 | { @"Æ", 198 }, 78 | { @"Ç", 199 }, 79 | { @"È", 200 }, 80 | { @"É", 201 }, 81 | { @"Ê", 202 }, 82 | { @"Ë", 203 }, 83 | { @"Ì", 204 }, 84 | { @"Í", 205 }, 85 | { @"Î", 206 }, 86 | { @"Ï", 207 }, 87 | { @"Ð", 208 }, 88 | { @"Ñ", 209 }, 89 | { @"Ò", 210 }, 90 | { @"Ó", 211 }, 91 | { @"Ô", 212 }, 92 | { @"Õ", 213 }, 93 | { @"Ö", 214 }, 94 | { @"×", 215 }, 95 | { @"Ø", 216 }, 96 | { @"Ù", 217 }, 97 | { @"Ú", 218 }, 98 | { @"Û", 219 }, 99 | { @"Ü", 220 }, 100 | { @"Ý", 221 }, 101 | { @"Þ", 222 }, 102 | { @"ß", 223 }, 103 | { @"à", 224 }, 104 | { @"á", 225 }, 105 | { @"â", 226 }, 106 | { @"ã", 227 }, 107 | { @"ä", 228 }, 108 | { @"å", 229 }, 109 | { @"æ", 230 }, 110 | { @"ç", 231 }, 111 | { @"è", 232 }, 112 | { @"é", 233 }, 113 | { @"ê", 234 }, 114 | { @"ë", 235 }, 115 | { @"ì", 236 }, 116 | { @"í", 237 }, 117 | { @"î", 238 }, 118 | { @"ï", 239 }, 119 | { @"ð", 240 }, 120 | { @"ñ", 241 }, 121 | { @"ò", 242 }, 122 | { @"ó", 243 }, 123 | { @"ô", 244 }, 124 | { @"õ", 245 }, 125 | { @"ö", 246 }, 126 | { @"÷", 247 }, 127 | { @"ø", 248 }, 128 | { @"ù", 249 }, 129 | { @"ú", 250 }, 130 | { @"û", 251 }, 131 | { @"ü", 252 }, 132 | { @"ý", 253 }, 133 | { @"þ", 254 }, 134 | { @"ÿ", 255 }, 135 | 136 | // A.2.2. Special characters cont'd 137 | { @"Œ", 338 }, 138 | { @"œ", 339 }, 139 | { @"Š", 352 }, 140 | { @"š", 353 }, 141 | { @"Ÿ", 376 }, 142 | 143 | // A.2.3. Symbols 144 | { @"ƒ", 402 }, 145 | 146 | // A.2.2. Special characters cont'd 147 | { @"ˆ", 710 }, 148 | { @"˜", 732 }, 149 | 150 | // A.2.3. Symbols cont'd 151 | { @"Α", 913 }, 152 | { @"Β", 914 }, 153 | { @"Γ", 915 }, 154 | { @"Δ", 916 }, 155 | { @"Ε", 917 }, 156 | { @"Ζ", 918 }, 157 | { @"Η", 919 }, 158 | { @"Θ", 920 }, 159 | { @"Ι", 921 }, 160 | { @"Κ", 922 }, 161 | { @"Λ", 923 }, 162 | { @"Μ", 924 }, 163 | { @"Ν", 925 }, 164 | { @"Ξ", 926 }, 165 | { @"Ο", 927 }, 166 | { @"Π", 928 }, 167 | { @"Ρ", 929 }, 168 | { @"Σ", 931 }, 169 | { @"Τ", 932 }, 170 | { @"Υ", 933 }, 171 | { @"Φ", 934 }, 172 | { @"Χ", 935 }, 173 | { @"Ψ", 936 }, 174 | { @"Ω", 937 }, 175 | { @"α", 945 }, 176 | { @"β", 946 }, 177 | { @"γ", 947 }, 178 | { @"δ", 948 }, 179 | { @"ε", 949 }, 180 | { @"ζ", 950 }, 181 | { @"η", 951 }, 182 | { @"θ", 952 }, 183 | { @"ι", 953 }, 184 | { @"κ", 954 }, 185 | { @"λ", 955 }, 186 | { @"μ", 956 }, 187 | { @"ν", 957 }, 188 | { @"ξ", 958 }, 189 | { @"ο", 959 }, 190 | { @"π", 960 }, 191 | { @"ρ", 961 }, 192 | { @"ς", 962 }, 193 | { @"σ", 963 }, 194 | { @"τ", 964 }, 195 | { @"υ", 965 }, 196 | { @"φ", 966 }, 197 | { @"χ", 967 }, 198 | { @"ψ", 968 }, 199 | { @"ω", 969 }, 200 | { @"ϑ", 977 }, 201 | { @"ϒ", 978 }, 202 | { @"ϖ", 982 }, 203 | 204 | // A.2.2. Special characters cont'd 205 | { @" ", 8194 }, 206 | { @" ", 8195 }, 207 | { @" ", 8201 }, 208 | { @"‌", 8204 }, 209 | { @"‍", 8205 }, 210 | { @"‎", 8206 }, 211 | { @"‏", 8207 }, 212 | { @"–", 8211 }, 213 | { @"—", 8212 }, 214 | { @"‘", 8216 }, 215 | { @"’", 8217 }, 216 | { @"‚", 8218 }, 217 | { @"“", 8220 }, 218 | { @"”", 8221 }, 219 | { @"„", 8222 }, 220 | { @"†", 8224 }, 221 | { @"‡", 8225 }, 222 | // A.2.3. Symbols cont'd 223 | { @"•", 8226 }, 224 | { @"…", 8230 }, 225 | 226 | // A.2.2. Special characters cont'd 227 | { @"‰", 8240 }, 228 | 229 | // A.2.3. Symbols cont'd 230 | { @"′", 8242 }, 231 | { @"″", 8243 }, 232 | 233 | // A.2.2. Special characters cont'd 234 | { @"‹", 8249 }, 235 | { @"›", 8250 }, 236 | 237 | // A.2.3. Symbols cont'd 238 | { @"‾", 8254 }, 239 | { @"⁄", 8260 }, 240 | 241 | // A.2.2. Special characters cont'd 242 | { @"€", 8364 }, 243 | 244 | // A.2.3. Symbols cont'd 245 | { @"ℑ", 8465 }, 246 | { @"℘", 8472 }, 247 | { @"ℜ", 8476 }, 248 | { @"™", 8482 }, 249 | { @"ℵ", 8501 }, 250 | { @"←", 8592 }, 251 | { @"↑", 8593 }, 252 | { @"→", 8594 }, 253 | { @"↓", 8595 }, 254 | { @"↔", 8596 }, 255 | { @"↵", 8629 }, 256 | { @"⇐", 8656 }, 257 | { @"⇑", 8657 }, 258 | { @"⇒", 8658 }, 259 | { @"⇓", 8659 }, 260 | { @"⇔", 8660 }, 261 | { @"∀", 8704 }, 262 | { @"∂", 8706 }, 263 | { @"∃", 8707 }, 264 | { @"∅", 8709 }, 265 | { @"∇", 8711 }, 266 | { @"∈", 8712 }, 267 | { @"∉", 8713 }, 268 | { @"∋", 8715 }, 269 | { @"∏", 8719 }, 270 | { @"∑", 8721 }, 271 | { @"−", 8722 }, 272 | { @"∗", 8727 }, 273 | { @"√", 8730 }, 274 | { @"∝", 8733 }, 275 | { @"∞", 8734 }, 276 | { @"∠", 8736 }, 277 | { @"∧", 8743 }, 278 | { @"∨", 8744 }, 279 | { @"∩", 8745 }, 280 | { @"∪", 8746 }, 281 | { @"∫", 8747 }, 282 | { @"∴", 8756 }, 283 | { @"∼", 8764 }, 284 | { @"≅", 8773 }, 285 | { @"≈", 8776 }, 286 | { @"≠", 8800 }, 287 | { @"≡", 8801 }, 288 | { @"≤", 8804 }, 289 | { @"≥", 8805 }, 290 | { @"⊂", 8834 }, 291 | { @"⊃", 8835 }, 292 | { @"⊄", 8836 }, 293 | { @"⊆", 8838 }, 294 | { @"⊇", 8839 }, 295 | { @"⊕", 8853 }, 296 | { @"⊗", 8855 }, 297 | { @"⊥", 8869 }, 298 | { @"⋅", 8901 }, 299 | { @"⌈", 8968 }, 300 | { @"⌉", 8969 }, 301 | { @"⌊", 8970 }, 302 | { @"⌋", 8971 }, 303 | { @"⟨", 9001 }, 304 | { @"⟩", 9002 }, 305 | { @"◊", 9674 }, 306 | { @"♠", 9824 }, 307 | { @"♣", 9827 }, 308 | { @"♥", 9829 }, 309 | { @"♦", 9830 } 310 | }; 311 | 312 | // Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 313 | // This is table A.2.2 Special Characters 314 | static HTMLEscapeMap gUnicodeHTMLEscapeMap[] = { 315 | // C0 Controls and Basic Latin 316 | { @""", 34 }, 317 | { @"&", 38 }, 318 | { @"'", 39 }, 319 | { @"<", 60 }, 320 | { @">", 62 }, 321 | 322 | // Latin Extended-A 323 | { @"Œ", 338 }, 324 | { @"œ", 339 }, 325 | { @"Š", 352 }, 326 | { @"š", 353 }, 327 | { @"Ÿ", 376 }, 328 | 329 | // Spacing Modifier Letters 330 | { @"ˆ", 710 }, 331 | { @"˜", 732 }, 332 | 333 | // General Punctuation 334 | { @" ", 8194 }, 335 | { @" ", 8195 }, 336 | { @" ", 8201 }, 337 | { @"‌", 8204 }, 338 | { @"‍", 8205 }, 339 | { @"‎", 8206 }, 340 | { @"‏", 8207 }, 341 | { @"–", 8211 }, 342 | { @"—", 8212 }, 343 | { @"‘", 8216 }, 344 | { @"’", 8217 }, 345 | { @"‚", 8218 }, 346 | { @"“", 8220 }, 347 | { @"”", 8221 }, 348 | { @"„", 8222 }, 349 | { @"†", 8224 }, 350 | { @"‡", 8225 }, 351 | { @"‰", 8240 }, 352 | { @"‹", 8249 }, 353 | { @"›", 8250 }, 354 | { @"€", 8364 }, 355 | }; 356 | 357 | 358 | // Utility function for Bsearching table above 359 | static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) { 360 | const unichar *uchar = (const unichar*)ucharVoid; 361 | const HTMLEscapeMap *map = (const HTMLEscapeMap*)mapVoid; 362 | int val; 363 | if (*uchar > map->uchar) { 364 | val = 1; 365 | } else if (*uchar < map->uchar) { 366 | val = -1; 367 | } else { 368 | val = 0; 369 | } 370 | return val; 371 | } 372 | 373 | @implementation NSString (GTMNSStringHTMLAdditions) 374 | 375 | - (NSString *)gtm_stringByEscapingHTMLUsingTable:(HTMLEscapeMap*)table 376 | ofSize:(NSUInteger)size 377 | escapingUnicode:(BOOL)escapeUnicode { 378 | NSUInteger length = [self length]; 379 | if (!length) { 380 | return self; 381 | } 382 | 383 | NSMutableString *finalString = [NSMutableString string]; 384 | NSMutableData *data2 = [NSMutableData dataWithCapacity:sizeof(unichar) * length]; 385 | 386 | // this block is common between GTMNSString+HTML and GTMNSString+XML but 387 | // it's so short that it isn't really worth trying to share. 388 | const unichar *buffer = CFStringGetCharactersPtr((CFStringRef)self); 389 | if (!buffer) { 390 | // We want this buffer to be autoreleased. 391 | NSMutableData *data = [NSMutableData dataWithLength:length * sizeof(UniChar)]; 392 | if (!data) { 393 | // COV_NF_START - Memory fail case 394 | // _GTMDevLog(@"couldn't alloc buffer"); 395 | return nil; 396 | // COV_NF_END 397 | } 398 | [self getCharacters:[data mutableBytes]]; 399 | buffer = [data bytes]; 400 | } 401 | 402 | if (!buffer || !data2) { 403 | // COV_NF_START 404 | // _GTMDevLog(@"Unable to allocate buffer or data2"); 405 | return nil; 406 | // COV_NF_END 407 | } 408 | 409 | unichar *buffer2 = (unichar *)[data2 mutableBytes]; 410 | 411 | NSUInteger buffer2Length = 0; 412 | 413 | for (NSUInteger i = 0; i < length; ++i) { 414 | HTMLEscapeMap *val = bsearch(&buffer[i], table, 415 | size / sizeof(HTMLEscapeMap), 416 | sizeof(HTMLEscapeMap), EscapeMapCompare); 417 | if (val || (escapeUnicode && buffer[i] > 127)) { 418 | if (buffer2Length) { 419 | CFStringAppendCharacters((CFMutableStringRef)finalString, 420 | buffer2, 421 | buffer2Length); 422 | buffer2Length = 0; 423 | } 424 | if (val) { 425 | [finalString appendString:val->escapeSequence]; 426 | } 427 | else { 428 | // _GTMDevAssert(escapeUnicode && buffer[i] > 127, @"Illegal Character"); 429 | [finalString appendFormat:@"&#%d;", buffer[i]]; 430 | } 431 | } else { 432 | buffer2[buffer2Length] = buffer[i]; 433 | buffer2Length += 1; 434 | } 435 | } 436 | if (buffer2Length) { 437 | CFStringAppendCharacters((CFMutableStringRef)finalString, 438 | buffer2, 439 | buffer2Length); 440 | } 441 | return finalString; 442 | } 443 | 444 | - (NSString *)gtm_stringByEscapingForHTML { 445 | return [self gtm_stringByEscapingHTMLUsingTable:gUnicodeHTMLEscapeMap 446 | ofSize:sizeof(gUnicodeHTMLEscapeMap) 447 | escapingUnicode:NO]; 448 | } // gtm_stringByEscapingHTML 449 | 450 | - (NSString *)gtm_stringByEscapingForAsciiHTML { 451 | return [self gtm_stringByEscapingHTMLUsingTable:gAsciiHTMLEscapeMap 452 | ofSize:sizeof(gAsciiHTMLEscapeMap) 453 | escapingUnicode:YES]; 454 | } // gtm_stringByEscapingAsciiHTML 455 | 456 | - (NSString *)gtm_stringByUnescapingFromHTML { 457 | NSRange range = NSMakeRange(0, [self length]); 458 | NSRange subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]; 459 | 460 | // if no ampersands, we've got a quick way out 461 | if (subrange.length == 0) return self; 462 | NSMutableString *finalString = [NSMutableString stringWithString:self]; 463 | do { 464 | NSRange semiColonRange = NSMakeRange(subrange.location, NSMaxRange(range) - subrange.location); 465 | semiColonRange = [self rangeOfString:@";" options:0 range:semiColonRange]; 466 | range = NSMakeRange(0, subrange.location); 467 | // if we don't find a semicolon in the range, we don't have a sequence 468 | if (semiColonRange.location == NSNotFound) { 469 | continue; 470 | } 471 | NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1); 472 | NSString *escapeString = [self substringWithRange:escapeRange]; 473 | NSUInteger length = [escapeString length]; 474 | // a squence must be longer than 3 (<) and less than 11 (ϑ) 475 | if (length > 3 && length < 11) { 476 | if ([escapeString characterAtIndex:1] == '#') { 477 | unichar char2 = [escapeString characterAtIndex:2]; 478 | if (char2 == 'x' || char2 == 'X') { 479 | // Hex escape squences £ 480 | NSString *hexSequence = [escapeString substringWithRange:NSMakeRange(3, length - 4)]; 481 | NSScanner *scanner = [NSScanner scannerWithString:hexSequence]; 482 | unsigned value; 483 | if ([scanner scanHexInt:&value] && 484 | value < USHRT_MAX && 485 | value > 0 486 | && [scanner scanLocation] == length - 4) { 487 | unichar uchar = value; 488 | NSString *charString = [NSString stringWithCharacters:&uchar length:1]; 489 | [finalString replaceCharactersInRange:escapeRange withString:charString]; 490 | } 491 | 492 | } else { 493 | // Decimal Sequences { 494 | NSString *numberSequence = [escapeString substringWithRange:NSMakeRange(2, length - 3)]; 495 | NSScanner *scanner = [NSScanner scannerWithString:numberSequence]; 496 | int value; 497 | if ([scanner scanInt:&value] && 498 | value < USHRT_MAX && 499 | value > 0 500 | && [scanner scanLocation] == length - 3) { 501 | unichar uchar = value; 502 | NSString *charString = [NSString stringWithCharacters:&uchar length:1]; 503 | [finalString replaceCharactersInRange:escapeRange withString:charString]; 504 | } 505 | } 506 | } else { 507 | // "standard" sequences 508 | for (unsigned i = 0; i < sizeof(gAsciiHTMLEscapeMap) / sizeof(HTMLEscapeMap); ++i) { 509 | if ([escapeString isEqualToString:gAsciiHTMLEscapeMap[i].escapeSequence]) { 510 | [finalString replaceCharactersInRange:escapeRange withString:[NSString stringWithCharacters:&gAsciiHTMLEscapeMap[i].uchar length:1]]; 511 | break; 512 | } 513 | } 514 | } 515 | } 516 | } while ((subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]).length != 0); 517 | return finalString; 518 | } // gtm_stringByUnescapingHTML 519 | 520 | 521 | 522 | @end --------------------------------------------------------------------------------