├── .covio.yml ├── .gitignore ├── .travis.yml ├── Interpreter.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Node.xcscheme ├── Interpreter.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── WorkspaceSettings.xcsettings ├── NodelikeDemo ├── Base.lproj │ └── Main.storyboard ├── CSNotificationView.h ├── CSNotificationView.m ├── CSNotificationView.xcassets │ ├── CSNotificationView_checkmarkIcon.imageset │ │ ├── CSNotificationView_checkmarkIcon.png │ │ ├── CSNotificationView_checkmarkIcon@2x.png │ │ └── Contents.json │ └── CSNotificationView_exclamationMarkIcon.imageset │ │ ├── CSNotificationView_exclamationMarkIcon.png │ │ ├── CSNotificationView_exclamationMarkIcon@2x.png │ │ └── Contents.json ├── CYRLayoutManager.h ├── CYRLayoutManager.m ├── CYRTextStorage.h ├── CYRTextStorage.m ├── CYRTextView.h ├── CYRTextView.m ├── CYRToken.h ├── CYRToken.m ├── FHSegmentedViewController.h ├── FHSegmentedViewController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-60@2x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-Small-1.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x-1.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Spotlight-40.png │ │ ├── Icon-Spotlight-40@2x-1.png │ │ └── Icon-Spotlight-40@2x.png │ ├── KOKeyboard │ │ ├── hal-black.imageset │ │ │ ├── Contents.json │ │ │ ├── hal-black.png │ │ │ └── hal-black@2x.png │ │ ├── hal-blue.imageset │ │ │ ├── Contents.json │ │ │ ├── hal-blue.png │ │ │ └── hal-blue@2x.png │ │ └── hal-white.imageset │ │ │ ├── Contents.json │ │ │ ├── hal-white.png │ │ │ └── hal-white@2x.png │ ├── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── launch-pad-land.png │ │ ├── launch-pad-land@2x.png │ │ ├── launch-pad.png │ │ ├── launch-pad@2x.png │ │ ├── launch-phone-r4-1.png │ │ └── launch-phone@2x.png │ ├── bookmarks.imageset │ │ ├── Contents.json │ │ ├── bookmarks.png │ │ └── bookmarks@2x.png │ └── info.imageset │ │ ├── Contents.json │ │ ├── info.png │ │ └── info@2x.png ├── Interpreter-Info.plist ├── Interpreter-Prefix.pch ├── LaunchScreen.xib ├── NLAppDelegate.h ├── NLAppDelegate.m ├── NLColor.h ├── NLColor.m ├── NLConsoleViewController.h ├── NLConsoleViewController.m ├── NLDocumentationViewController.h ├── NLDocumentationViewController.m ├── NLEditorViewController.h ├── NLEditorViewController.m ├── NLMasterViewController.h ├── NLMasterViewController.m ├── NLTextView.h ├── NLTextView.m ├── PBWebViewController.h ├── PBWebViewController.m ├── SEJSONViewController.h ├── SEJSONViewController.m ├── Syntax.plist ├── Vendor │ └── KOKeyboard │ │ ├── KOKeyboardRow.h │ │ ├── KOKeyboardRow.m │ │ ├── KOSwipeButton.h │ │ └── KOSwipeButton.m ├── en.lproj │ └── InfoPlist.strings └── main.m ├── Podfile ├── README.md ├── demo.png └── node_modules └── examples ├── index.js └── node_modules ├── example01 └── index.js ├── example02 └── index.js ├── example03 └── index.js └── example04 └── index.js /.covio.yml: -------------------------------------------------------------------------------- 1 | violations: 2 | sloccount: sloccount Nodelike 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata 3 | 4 | *.xccheckout 5 | build 6 | 7 | Podfile.lock 8 | Pods/ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_sdk: iphonesimulator 3 | xcode_scheme: Node 4 | xcode_workspace: Interpreter.xcworkspace 5 | rvm: 1.9.3 6 | before_install: gem install cocoapods 7 | notifications: 8 | flowdock: 803358c5edc46b50866b8e588b4c374b 9 | webhooks: 10 | urls: 11 | - https://webhooks.gitter.im/e/fa1f3636231a3e89e7fd 12 | on_success: change 13 | on_failure: always 14 | on_start: false 15 | hipchat: 16 | secure: aWO9pLQ74SvlXTUMgXrIrezP5VQZdJE2gNgfmmOoK6S6qUiRALJi0wSF4njv1WFf9ZLWaWr8b7Z7Gs4ZeF+rhI7/xGw7YdQaJYkiQ/yo0QdUhn5NTgzJo8CMyremNJx/qcK0+FbwsttIpB7kYWW9XuFGUrmHGojX5Lx1pYnFbZk= 17 | -------------------------------------------------------------------------------- /Interpreter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Interpreter.xcodeproj/xcshareddata/xcschemes/Node.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Interpreter.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Interpreter.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NodelikeDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSNotificationView.h 3 | // CSNotificationView 4 | // 5 | // Created by Christian Schwarz on 01.09.13. 6 | // Copyright (c) 2013 Christian Schwarz. Check LICENSE.md. 7 | // 8 | 9 | #import 10 | 11 | static CGFloat const kCSNotificationViewHeight = 50.0f; 12 | static CGFloat const kCSNotificationViewSymbolViewSidelength = 44.0f; 13 | static NSTimeInterval const kCSNotificationViewDefaultShowDuration = 2.0; 14 | 15 | typedef enum { 16 | CSNotificationViewStyleSuccess, 17 | CSNotificationViewStyleError, 18 | } CSNotificationViewStyle; 19 | 20 | @interface CSNotificationView : UIView 21 | 22 | #pragma mark + quick presentation 23 | 24 | + (void)showInViewController:(UIViewController*)viewController 25 | style:(CSNotificationViewStyle)style 26 | message:(NSString*)message; 27 | 28 | + (void)showInViewController:(UIViewController*)viewController 29 | tintColor:(UIColor*)tintColor 30 | image:(UIImage*)image 31 | message:(NSString*)message 32 | duration:(NSTimeInterval)duration; 33 | 34 | #pragma mark + creators 35 | 36 | + (CSNotificationView*)notificationViewWithParentViewController:(UIViewController*)viewController 37 | tintColor:(UIColor*)tintColor 38 | image:(UIImage*)image 39 | message:(NSString*)message; 40 | 41 | #pragma mark - initialization 42 | 43 | - (instancetype)initWithParentViewController:(UIViewController*)viewController; 44 | 45 | #pragma mark - presentation 46 | 47 | - (void)setVisible:(BOOL)showing animated:(BOOL)animated completion:(void (^)())completion; 48 | - (void)dismissWithStyle:(CSNotificationViewStyle)style message:(NSString*)message duration:(NSTimeInterval)duration animated:(BOOL)animated; 49 | @property (readonly, nonatomic, getter = isShowing) BOOL visible; 50 | 51 | #pragma mark - visible properties 52 | 53 | /** 54 | The image property should be used for setting the image displayed in imageView 55 | Only the alpha value will be used and then be tinted to a 'legible' color 56 | */ 57 | @property (nonatomic, strong) UIImage* image; 58 | 59 | @property (nonatomic, strong) UIColor* tintColor; 60 | 61 | @property (nonatomic, getter = isShowingActivity) BOOL showingActivity; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSNotificationView.m 3 | // CSNotificationView 4 | // 5 | // Created by Christian Schwarz on 01.09.13. 6 | // Copyright (c) 2013 Christian Schwarz. Check LICENSE.md. 7 | // 8 | 9 | #import "CSNotificationView.h" 10 | 11 | static NSInteger const kCSNotificationViewEmptySymbolViewTag = 666; 12 | 13 | @interface CSNotificationView () 14 | 15 | #pragma mark - blur effect 16 | @property (nonatomic, strong) UIToolbar *toolbar; 17 | @property (nonatomic, strong) CALayer *blurLayer; 18 | 19 | #pragma mark - presentation 20 | @property (nonatomic, weak) UIViewController* parentViewController; 21 | @property (nonatomic, weak) UINavigationController* parentNavigationController; 22 | @property (nonatomic, getter = isVisible) BOOL visible; 23 | 24 | #pragma mark - content views 25 | @property (nonatomic, strong, readonly) UIView* symbolView; // is updated by -(void)updateSymbolView 26 | @property (nonatomic, strong) UILabel* textLabel; 27 | @property (nonatomic, strong) UIColor* contentColor; 28 | 29 | @end 30 | 31 | @implementation CSNotificationView 32 | 33 | #pragma mark + quick presentation 34 | 35 | + (void)showInViewController:(UIViewController*)viewController 36 | tintColor:(UIColor*)tintColor 37 | image:(UIImage*)image 38 | message:(NSString*)message 39 | duration:(NSTimeInterval)duration 40 | { 41 | NSAssert(message, @"'message' must not be nil."); 42 | 43 | __block CSNotificationView* note = [[CSNotificationView alloc] initWithParentViewController:viewController]; 44 | note.tintColor = tintColor; 45 | note.image = image; 46 | note.textLabel.text = message; 47 | 48 | void (^completion)() = ^{[note setVisible:NO animated:YES completion:nil];}; 49 | [note setVisible:YES animated:YES completion:^{ 50 | double delayInSeconds = duration; 51 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 52 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 53 | completion(); 54 | }); 55 | }]; 56 | 57 | } 58 | 59 | + (void)showInViewController:(UIViewController *)viewController 60 | style:(CSNotificationViewStyle)style 61 | message:(NSString *)message 62 | { 63 | 64 | 65 | [CSNotificationView showInViewController:viewController 66 | tintColor:[CSNotificationView blurTintColorForStyle:style] 67 | image:[CSNotificationView imageForStyle:style] 68 | message:message 69 | duration:kCSNotificationViewDefaultShowDuration]; 70 | } 71 | 72 | #pragma mark + creators 73 | 74 | + (CSNotificationView*)notificationViewWithParentViewController:(UIViewController*)viewController 75 | tintColor:(UIColor*)tintColor 76 | image:(UIImage*)image 77 | message:(NSString*)message 78 | { 79 | NSParameterAssert(viewController); 80 | 81 | CSNotificationView* note = [[CSNotificationView alloc] initWithParentViewController:viewController]; 82 | note.tintColor = tintColor; 83 | note.image = image; 84 | note.textLabel.text = message; 85 | 86 | return note; 87 | } 88 | 89 | #pragma mark - lifecycle 90 | 91 | - (instancetype)initWithParentViewController:(UIViewController*)viewController 92 | { 93 | self = [super initWithFrame:CGRectZero]; 94 | if (self) { 95 | 96 | //Blur | thanks to https://github.com/JagCesar/iOS-blur for providing this under the WTFPL-license! 97 | { 98 | [self setToolbar:[[UIToolbar alloc] initWithFrame:[self bounds]]]; 99 | [self setBlurLayer:[[self toolbar] layer]]; 100 | 101 | UIView *blurView = [UIView new]; 102 | [blurView setUserInteractionEnabled:NO]; 103 | [blurView.layer addSublayer:[self blurLayer]]; 104 | [blurView setTranslatesAutoresizingMaskIntoConstraints:NO]; 105 | [blurView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 106 | [self insertSubview:blurView atIndex:0]; 107 | 108 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[blurView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(blurView)]]; 109 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(-1)-[blurView]-(-1)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(blurView)]]; 110 | 111 | [self setBackgroundColor:[UIColor clearColor]]; 112 | } 113 | 114 | //Parent view 115 | { 116 | self.parentViewController = viewController; 117 | 118 | NSAssert(!([self.parentViewController isKindOfClass:[UITableViewController class]] && !self.parentViewController.navigationController), @"Due to a bug in iOS 7.0.1|2|3 UITableViewController, CSNotificationView cannot present in UITableViewController without a parent UINavigationController"); 119 | 120 | if (self.parentViewController.navigationController) { 121 | self.parentNavigationController = self.parentViewController.navigationController; 122 | } 123 | if ([self.parentViewController isKindOfClass:[UINavigationController class]]) { 124 | self.parentNavigationController = (UINavigationController*)self.parentViewController; 125 | } 126 | 127 | } 128 | 129 | //Content views 130 | { 131 | //textLabel 132 | { 133 | _textLabel = [[UILabel alloc] init]; 134 | 135 | UIFontDescriptor* textLabelFontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody]; 136 | _textLabel.font = [UIFont fontWithDescriptor:textLabelFontDescriptor size:17.0f]; 137 | _textLabel.minimumScaleFactor = 0.6; 138 | _textLabel.lineBreakMode = NSLineBreakByTruncatingTail; 139 | _textLabel.adjustsFontSizeToFitWidth = YES; 140 | 141 | _textLabel.numberOfLines = 2; 142 | _textLabel.textColor = [UIColor whiteColor]; 143 | _textLabel.translatesAutoresizingMaskIntoConstraints = NO; 144 | [self addSubview:_textLabel]; 145 | } 146 | //symbolView 147 | { 148 | [self updateSymbolView]; 149 | } 150 | } 151 | 152 | } 153 | return self; 154 | } 155 | 156 | #pragma mark - layout 157 | 158 | - (void)updateConstraints 159 | { 160 | [self removeConstraints:self.constraints]; 161 | 162 | CGFloat symbolViewWidth = self.symbolView.tag != kCSNotificationViewEmptySymbolViewTag ? 163 | kCSNotificationViewSymbolViewSidelength : 0.0f; 164 | CGFloat symbolViewHeight = kCSNotificationViewSymbolViewSidelength; 165 | 166 | NSDictionary* metrics = 167 | @{@"symbolViewWidth": [NSNumber numberWithFloat:symbolViewWidth], 168 | @"symbolViewHeight":[NSNumber numberWithFloat:symbolViewHeight]}; 169 | 170 | [self addConstraints:[NSLayoutConstraint 171 | constraintsWithVisualFormat:@"H:|-(4)-[_symbolView(symbolViewWidth)]-(5)-[_textLabel]-(10)-|" 172 | options:0 173 | metrics:metrics 174 | views:NSDictionaryOfVariableBindings(_textLabel, _symbolView)]]; 175 | 176 | [self addConstraints:[NSLayoutConstraint 177 | constraintsWithVisualFormat:@"V:[_symbolView(symbolViewHeight)]" 178 | options:0 179 | metrics:metrics 180 | views:NSDictionaryOfVariableBindings(_symbolView)]]; 181 | 182 | CGFloat topInset = CGRectGetHeight(self.frame) - 4; 183 | 184 | [self addConstraint:[NSLayoutConstraint 185 | constraintWithItem:_symbolView 186 | attribute:NSLayoutAttributeBottom 187 | relatedBy:NSLayoutRelationEqual 188 | toItem:self 189 | attribute:NSLayoutAttributeBottom 190 | multiplier:0.0f constant:topInset]]; 191 | 192 | [self addConstraint:[NSLayoutConstraint 193 | constraintWithItem:_textLabel 194 | attribute:NSLayoutAttributeCenterY 195 | relatedBy:NSLayoutRelationEqual 196 | toItem:_symbolView 197 | attribute:NSLayoutAttributeCenterY 198 | multiplier:1.0f constant:0]]; 199 | 200 | [super updateConstraints]; 201 | } 202 | 203 | - (void)setFrame:(CGRect)frame 204 | { 205 | [super setFrame:frame]; 206 | self.blurLayer.frame = self.bounds; 207 | } 208 | 209 | #pragma mark - tint color 210 | 211 | - (void)setTintColor:(UIColor *)tintColor 212 | { 213 | _tintColor = tintColor; 214 | //Use 0.6 alpha value for translucency blur in UIToolbar 215 | [self.toolbar setBarTintColor:[tintColor colorWithAlphaComponent:0.6]]; 216 | self.contentColor = [self legibleTextColorForBlurTintColor:tintColor]; 217 | } 218 | 219 | #pragma mark - presentation 220 | 221 | - (void)setVisible:(BOOL)visible animated:(BOOL)animated completion:(void (^)())completion 222 | { 223 | if (_visible != visible) { 224 | 225 | NSTimeInterval animationDuration = animated ? 0.4 : 0.0; 226 | CGRect startFrame = visible ? [self hiddenFrame]:[self visibleFrame]; 227 | CGRect endFrame = visible ? [self visibleFrame] : [self hiddenFrame]; 228 | 229 | if (!self.superview) { 230 | self.frame = startFrame; 231 | 232 | if (self.parentNavigationController) { 233 | for (UIView* view in self.parentNavigationController.view.subviews) { 234 | if ([view isKindOfClass:[UINavigationBar class]]) { 235 | [self.parentNavigationController.view insertSubview:self belowSubview:view]; 236 | } 237 | } 238 | } else { 239 | [self.parentViewController.view addSubview:self]; 240 | } 241 | 242 | } 243 | 244 | __block typeof(self) weakself = self; 245 | [UIView animateWithDuration:animationDuration animations:^{ 246 | [weakself setFrame:endFrame]; 247 | } completion:^(BOOL finished) { 248 | if (!visible) { 249 | [weakself removeFromSuperview]; 250 | } 251 | if (completion) { 252 | completion(); 253 | } 254 | }]; 255 | 256 | _visible = visible; 257 | } else if (completion) { 258 | completion(); 259 | } 260 | } 261 | 262 | - (void)dismissWithStyle:(CSNotificationViewStyle)style message:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated 263 | { 264 | NSParameterAssert(message); 265 | 266 | __block typeof(self) weakself = self; 267 | [UIView animateWithDuration:0.1 animations:^{ 268 | 269 | weakself.showingActivity = NO; 270 | weakself.image = [CSNotificationView imageForStyle:style]; 271 | weakself.textLabel.text = message; 272 | weakself.tintColor = [CSNotificationView blurTintColorForStyle:style]; 273 | 274 | } completion:^(BOOL finished) { 275 | double delayInSeconds = 2.0; 276 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 277 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 278 | [weakself setVisible:NO animated:animated completion:nil]; 279 | }); 280 | }]; 281 | } 282 | 283 | #pragma mark - frame calculation 284 | 285 | //Workaround as there is a bug: sometimes, when accessing topLayoutGuide, it will render contentSize of UITableViewControllers to be {0, 0} 286 | - (CGFloat)topLayoutGuideLengthCalculation 287 | { 288 | CGFloat top = CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]); 289 | if (self.parentNavigationController) { 290 | 291 | for (UIView* view in self.parentNavigationController.view.subviews) { 292 | if ([view isKindOfClass:[UINavigationBar class]]) { 293 | top += CGRectGetHeight(view.frame); 294 | break; 295 | } 296 | } 297 | } 298 | return top; 299 | } 300 | 301 | - (CGRect)visibleFrame 302 | { 303 | UIViewController* viewController = self.parentViewController; 304 | 305 | CGFloat topLayoutGuideLength = [self topLayoutGuideLengthCalculation]; 306 | 307 | CGRect displayFrame = CGRectMake(0, 0, CGRectGetWidth(viewController.view.frame), 308 | kCSNotificationViewHeight + topLayoutGuideLength); 309 | 310 | return displayFrame; 311 | } 312 | 313 | - (CGRect)hiddenFrame 314 | { 315 | UIViewController* viewController = self.parentViewController; 316 | 317 | CGFloat topLayoutGuideLength = [self topLayoutGuideLengthCalculation]; 318 | 319 | CGRect offscreenFrame = CGRectMake(0, -kCSNotificationViewHeight - topLayoutGuideLength, 320 | CGRectGetWidth(viewController.view.frame), 321 | kCSNotificationViewHeight + topLayoutGuideLength); 322 | 323 | return offscreenFrame; 324 | } 325 | 326 | #pragma mark - symbol view 327 | 328 | - (void)updateSymbolView 329 | { 330 | [self.symbolView removeFromSuperview]; 331 | 332 | if (self.isShowingActivity) { 333 | UIActivityIndicatorView* indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; 334 | indicator.color = self.contentColor; 335 | [indicator startAnimating]; 336 | _symbolView = indicator; 337 | } else if (self.image) { 338 | //Generate UIImageView for symbolView 339 | UIImageView* imageView = [[UIImageView alloc] init]; 340 | imageView.opaque = NO; 341 | imageView.backgroundColor = [UIColor clearColor]; 342 | imageView.translatesAutoresizingMaskIntoConstraints = NO; 343 | imageView.contentMode = UIViewContentModeCenter; 344 | imageView.image = [self imageFromAlphaChannelOfImage:self.image replacementColor:self.contentColor]; 345 | _symbolView = imageView; 346 | } else { 347 | _symbolView = [[UIView alloc] initWithFrame:CGRectZero]; 348 | _symbolView.tag = kCSNotificationViewEmptySymbolViewTag; 349 | } 350 | _symbolView.translatesAutoresizingMaskIntoConstraints = NO; 351 | [self addSubview:_symbolView]; 352 | [self setNeedsUpdateConstraints]; 353 | 354 | } 355 | 356 | #pragma mark -- image 357 | 358 | - (void)setImage:(UIImage *)image 359 | { 360 | if (![_image isEqual:image]) { 361 | _image = image; 362 | [self updateSymbolView]; 363 | } 364 | } 365 | 366 | #pragma mark -- activity 367 | 368 | - (void)setShowingActivity:(BOOL)showingActivity 369 | { 370 | if (_showingActivity != showingActivity) { 371 | _showingActivity = showingActivity; 372 | [self updateSymbolView]; 373 | } 374 | } 375 | 376 | 377 | #pragma mark - content color 378 | 379 | - (void)setContentColor:(UIColor *)contentColor 380 | { 381 | if (![_contentColor isEqual:contentColor]) { 382 | _contentColor = contentColor; 383 | self.textLabel.textColor = _contentColor; 384 | [self updateSymbolView]; 385 | } 386 | } 387 | 388 | #pragma mark helpers 389 | 390 | - (UIColor*)legibleTextColorForBlurTintColor:(UIColor*)blurTintColor 391 | { 392 | CGFloat r, g, b, a; 393 | BOOL couldConvert = [blurTintColor getRed:&r green:&g blue:&b alpha:&a]; 394 | 395 | UIColor* textColor = [UIColor whiteColor]; 396 | 397 | CGFloat average = (r+g+b)/3.0; //Not considering alpha here, transperency is added by toolbar 398 | if (couldConvert && average > 0.65) //0.65 is mostly gut-feeling 399 | { 400 | textColor = [[UIColor alloc] initWithWhite:0.2 alpha:1.0]; 401 | } 402 | 403 | return textColor; 404 | } 405 | 406 | - (UIImage*)imageFromAlphaChannelOfImage:(UIImage*)image replacementColor:(UIColor*)tintColor 407 | { 408 | if (!image) return nil; 409 | NSParameterAssert([tintColor isKindOfClass:[UIColor class]]); 410 | 411 | //Credits: https://gist.github.com/omz/1102091 412 | CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); 413 | UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale); 414 | CGContextRef c = UIGraphicsGetCurrentContext(); 415 | [image drawInRect:rect]; 416 | CGContextSetFillColorWithColor(c, [tintColor CGColor]); 417 | CGContextSetBlendMode(c, kCGBlendModeSourceAtop); 418 | CGContextFillRect(c, rect); 419 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); 420 | UIGraphicsEndImageContext(); 421 | return result; 422 | } 423 | 424 | + (UIImage*)imageForStyle:(CSNotificationViewStyle)style 425 | { 426 | UIImage* matchedImage = nil; 427 | switch (style) { 428 | case CSNotificationViewStyleSuccess: 429 | matchedImage = [UIImage imageNamed:@"CSNotificationView_checkmarkIcon"]; 430 | break; 431 | case CSNotificationViewStyleError: 432 | matchedImage = [UIImage imageNamed:@"CSNotificationView_exclamationMarkIcon"]; 433 | break; 434 | default: 435 | break; 436 | } 437 | return matchedImage; 438 | } 439 | 440 | + (UIColor*)blurTintColorForStyle:(CSNotificationViewStyle)style 441 | { 442 | UIColor* blurTintColor; 443 | switch (style) { 444 | case CSNotificationViewStyleSuccess: 445 | blurTintColor = [UIColor colorWithRed:0.21 green:0.72 blue:0.00 alpha:1.0]; 446 | break; 447 | case CSNotificationViewStyleError: 448 | blurTintColor = [UIColor redColor]; 449 | break; 450 | default: 451 | break; 452 | } 453 | return blurTintColor; 454 | } 455 | 456 | @end 457 | -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_checkmarkIcon.imageset/CSNotificationView_checkmarkIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_checkmarkIcon.imageset/CSNotificationView_checkmarkIcon.png -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_checkmarkIcon.imageset/CSNotificationView_checkmarkIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_checkmarkIcon.imageset/CSNotificationView_checkmarkIcon@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_checkmarkIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "CSNotificationView_checkmarkIcon.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "CSNotificationView_checkmarkIcon@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_exclamationMarkIcon.imageset/CSNotificationView_exclamationMarkIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_exclamationMarkIcon.imageset/CSNotificationView_exclamationMarkIcon.png -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_exclamationMarkIcon.imageset/CSNotificationView_exclamationMarkIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_exclamationMarkIcon.imageset/CSNotificationView_exclamationMarkIcon@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/CSNotificationView.xcassets/CSNotificationView_exclamationMarkIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "CSNotificationView_exclamationMarkIcon.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "CSNotificationView_exclamationMarkIcon@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/CYRLayoutManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYRLayoutManager.h 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // Original implementation taken from: https://github.com/alldritt/TextKit_LineNumbers 14 | // 15 | // The MIT License (MIT) 16 | // 17 | // Copyright (c) 2014 Cyrillian, Inc. 18 | // 19 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 20 | // this software and associated documentation files (the "Software"), to deal in 21 | // the Software without restriction, including without limitation the rights to 22 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 23 | // the Software, and to permit persons to whom the Software is furnished to do so, 24 | // subject to the following conditions: 25 | // 26 | // The above copyright notice and this permission notice shall be included in all 27 | // copies or substantial portions of the Software. 28 | // 29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 31 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 32 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 33 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 34 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | #import 37 | 38 | @interface CYRLayoutManager : NSLayoutManager 39 | 40 | @property (nonatomic, strong) UIFont *lineNumberFont; 41 | @property (nonatomic, strong) UIColor *lineNumberColor; 42 | 43 | @property (nonatomic, readonly) CGFloat gutterWidth; 44 | @property (nonatomic, assign) NSRange selectedRange; 45 | 46 | - (CGRect)paragraphRectForRange:(NSRange)range; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRLayoutManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYRLayoutManager.h 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // Original implementation taken from: https://github.com/alldritt/TextKit_LineNumbers 14 | // 15 | // The MIT License (MIT) 16 | // 17 | // Copyright (c) 2014 Cyrillian, Inc. 18 | // 19 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 20 | // this software and associated documentation files (the "Software"), to deal in 21 | // the Software without restriction, including without limitation the rights to 22 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 23 | // the Software, and to permit persons to whom the Software is furnished to do so, 24 | // subject to the following conditions: 25 | // 26 | // The above copyright notice and this permission notice shall be included in all 27 | // copies or substantial portions of the Software. 28 | // 29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 31 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 32 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 33 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 34 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | #import "CYRLayoutManager.h" 37 | 38 | static CGFloat kMinimumGutterWidth = 30.f; 39 | 40 | @interface CYRLayoutManager () 41 | 42 | @property (nonatomic, assign) CGFloat gutterWidth; 43 | @property (nonatomic, assign) UIEdgeInsets lineAreaInset; 44 | 45 | @property (nonatomic) NSUInteger lastParaLocation; 46 | @property (nonatomic) NSUInteger lastParaNumber; 47 | 48 | @end 49 | 50 | @implementation CYRLayoutManager 51 | 52 | #pragma mark - Initialization & Setup 53 | 54 | - (instancetype)init 55 | { 56 | self = [super init]; 57 | 58 | if (self) 59 | { 60 | [self _commonSetup]; 61 | } 62 | 63 | return self; 64 | } 65 | 66 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 67 | { 68 | self = [super initWithCoder:aDecoder]; 69 | 70 | if (self) 71 | { 72 | [self _commonSetup]; 73 | } 74 | 75 | return self; 76 | } 77 | 78 | - (void)_commonSetup 79 | { 80 | self.gutterWidth = kMinimumGutterWidth; 81 | self.selectedRange = NSMakeRange(0, 0); 82 | 83 | self.lineAreaInset = UIEdgeInsetsMake(0, 10, 0, 4); 84 | self.lineNumberColor = [UIColor grayColor]; 85 | self.lineNumberFont = [UIFont systemFontOfSize:10.0f]; 86 | } 87 | 88 | 89 | #pragma mark - Convenience 90 | 91 | - (CGRect)paragraphRectForRange:(NSRange)range 92 | { 93 | range = [self.textStorage.string paragraphRangeForRange:range]; 94 | range = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL]; 95 | 96 | CGRect startRect = [self lineFragmentRectForGlyphAtIndex:range.location effectiveRange:NULL]; 97 | CGRect endRect = [self lineFragmentRectForGlyphAtIndex:range.location + range.length - 1 effectiveRange:NULL]; 98 | 99 | CGRect paragraphRectForRange = CGRectUnion(startRect, endRect); 100 | paragraphRectForRange = CGRectOffset(paragraphRectForRange, _gutterWidth, 8); 101 | 102 | return paragraphRectForRange; 103 | } 104 | 105 | - (NSUInteger) _paraNumberForRange:(NSRange) charRange 106 | { 107 | // NSString does not provide a means of efficiently determining the paragraph number of a range of text. This code 108 | // attempts to optimize what would normally be a series linear searches by keeping track of the last paragraph number 109 | // found and uses that as the starting point for next paragraph number search. This works (mostly) because we 110 | // are generally asked for continguous increasing sequences of paragraph numbers. Also, this code is called in the 111 | // course of drawing a pagefull of text, and so even when moving back, the number of paragraphs to search for is 112 | // relativly low, even in really long bodies of text. 113 | // 114 | // This all falls down when the user edits the text, and can potentially invalidate the cached paragraph number which 115 | // causes a (potentially lengthy) search from the beginning of the string. 116 | 117 | if (charRange.location == self.lastParaLocation) 118 | return self.lastParaNumber; 119 | else if (charRange.location < self.lastParaLocation) 120 | { 121 | // We need to look backwards from the last known paragraph for the new paragraph range. This generally happens 122 | // when the text in the UITextView scrolls downward, revaling paragraphs before/above the ones previously drawn. 123 | 124 | NSString* s = self.textStorage.string; 125 | __block NSUInteger paraNumber = self.lastParaNumber; 126 | 127 | [s enumerateSubstringsInRange:NSMakeRange(charRange.location, self.lastParaLocation - charRange.location) 128 | options:NSStringEnumerationByParagraphs | 129 | NSStringEnumerationSubstringNotRequired | 130 | NSStringEnumerationReverse 131 | usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop){ 132 | if (enclosingRange.location <= charRange.location) { 133 | *stop = YES; 134 | } 135 | --paraNumber; 136 | }]; 137 | 138 | self.lastParaLocation = charRange.location; 139 | self.lastParaNumber = paraNumber; 140 | 141 | return paraNumber; 142 | } 143 | else 144 | { 145 | // We need to look forward from the last known paragraph for the new paragraph range. This generally happens 146 | // when the text in the UITextView scrolls upwards, revealing paragraphs that follow the ones previously drawn. 147 | 148 | NSString* s = self.textStorage.string; 149 | __block NSUInteger paraNumber = self.lastParaNumber; 150 | 151 | [s enumerateSubstringsInRange:NSMakeRange(self.lastParaLocation, charRange.location - self.lastParaLocation) 152 | options:NSStringEnumerationByParagraphs | NSStringEnumerationSubstringNotRequired 153 | usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop){ 154 | if (enclosingRange.location >= charRange.location) { 155 | *stop = YES; 156 | } 157 | ++paraNumber; 158 | }]; 159 | 160 | self.lastParaLocation = charRange.location; 161 | self.lastParaNumber = paraNumber; 162 | 163 | return paraNumber; 164 | } 165 | } 166 | 167 | 168 | #pragma mark - Layouting 169 | 170 | - (void)processEditingForTextStorage:(NSTextStorage *)textStorage edited:(NSTextStorageEditActions)editMask range:(NSRange)newCharRange changeInLength:(NSInteger)delta invalidatedRange:(NSRange)invalidatedCharRange 171 | { 172 | [super processEditingForTextStorage:textStorage edited:editMask range:newCharRange changeInLength:delta invalidatedRange:invalidatedCharRange]; 173 | 174 | if (invalidatedCharRange.location < self.lastParaLocation) 175 | { 176 | // When the backing store is edited ahead the cached paragraph location, invalidate the cache and force a complete 177 | // recalculation. We cannot be much smarter than this because we don't know how many paragraphs have been deleted 178 | // since the text has already been removed from the backing store. 179 | self.lastParaLocation = 0; 180 | self.lastParaNumber = 0; 181 | } 182 | } 183 | 184 | 185 | #pragma mark - Drawing 186 | 187 | - (void) drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin 188 | { 189 | [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin]; 190 | 191 | // Draw line numbers. Note that the background for line number gutter is drawn by the LineNumberTextView class. 192 | NSDictionary* atts = @{NSFontAttributeName : _lineNumberFont , 193 | NSForegroundColorAttributeName : _lineNumberColor}; 194 | 195 | [self enumerateLineFragmentsForGlyphRange:glyphsToShow 196 | usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { 197 | NSRange charRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:nil]; 198 | NSRange paraRange = [self.textStorage.string paragraphRangeForRange:charRange]; 199 | 200 | BOOL showCursorRect = NSLocationInRange(_selectedRange.location, paraRange); 201 | 202 | if (showCursorRect) 203 | { 204 | CGContextRef context = UIGraphicsGetCurrentContext(); 205 | CGRect cursorRect = CGRectMake(0, usedRect.origin.y + 8, _gutterWidth, usedRect.size.height); 206 | 207 | CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:0.9 alpha:1].CGColor); 208 | CGContextFillRect(context, cursorRect); 209 | } 210 | 211 | // Only draw line numbers for the paragraph's first line fragment. Subsequent fragments are wrapped portions of the paragraph and don't get the line number. 212 | if (charRange.location == paraRange.location) { 213 | CGRect gutterRect = CGRectOffset(CGRectMake(0, rect.origin.y, _gutterWidth, rect.size.height), origin.x, origin.y); 214 | NSUInteger paraNumber = [self _paraNumberForRange:charRange]; 215 | NSString* ln = [NSString stringWithFormat:@"%ld", (unsigned long) paraNumber + 1]; 216 | CGSize size = [ln sizeWithAttributes:atts]; 217 | 218 | [ln drawInRect:CGRectOffset(gutterRect, CGRectGetWidth(gutterRect) - _lineAreaInset.right - size.width - _gutterWidth, (CGRectGetHeight(gutterRect) - size.height) / 2.0) 219 | withAttributes:atts]; 220 | } 221 | 222 | }]; 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRTextStorage.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextStorage.h 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // 14 | // The MIT License (MIT) 15 | // 16 | // Copyright (c) 2014 Cyrillian, Inc. 17 | // 18 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | // this software and associated documentation files (the "Software"), to deal in 20 | // the Software without restriction, including without limitation the rights to 21 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 22 | // the Software, and to permit persons to whom the Software is furnished to do so, 23 | // subject to the following conditions: 24 | // 25 | // The above copyright notice and this permission notice shall be included in all 26 | // copies or substantial portions of the Software. 27 | // 28 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 30 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 31 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 32 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | #import 36 | 37 | @interface CYRTextStorage : NSTextStorage 38 | 39 | @property (nonatomic, strong) NSArray *tokens; 40 | @property (nonatomic, strong) UIFont *defaultFont; 41 | 42 | - (void)update; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRTextStorage.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextStorage.m 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // 14 | // The MIT License (MIT) 15 | // 16 | // Copyright (c) 2014 Cyrillian, Inc. 17 | // 18 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | // this software and associated documentation files (the "Software"), to deal in 20 | // the Software without restriction, including without limitation the rights to 21 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 22 | // the Software, and to permit persons to whom the Software is furnished to do so, 23 | // subject to the following conditions: 24 | // 25 | // The above copyright notice and this permission notice shall be included in all 26 | // copies or substantial portions of the Software. 27 | // 28 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 30 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 31 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 32 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | #import "CYRTextStorage.h" 36 | #import "CYRToken.h" 37 | 38 | @interface CYRTextStorage () 39 | 40 | @property (nonatomic, strong) NSMutableAttributedString *attributedString; 41 | @property (nonatomic, strong) NSMutableDictionary *regularExpressionCache; 42 | 43 | @end 44 | 45 | @implementation CYRTextStorage 46 | 47 | #pragma mark - Initialization & Setup 48 | 49 | - (id)init 50 | { 51 | if (self = [super init]) 52 | { 53 | _defaultFont = [UIFont systemFontOfSize:12.0f]; 54 | _attributedString = [NSMutableAttributedString new]; 55 | 56 | _tokens = @[]; 57 | _regularExpressionCache = @{}.mutableCopy; 58 | } 59 | 60 | return self; 61 | } 62 | 63 | 64 | #pragma mark - Overrides 65 | 66 | - (void)setTokens:(NSMutableArray *)tokens 67 | { 68 | _tokens = tokens; 69 | 70 | // Clear the regular expression cache 71 | [self.regularExpressionCache removeAllObjects]; 72 | 73 | // Redraw all text 74 | [self update]; 75 | } 76 | 77 | - (NSString *)string 78 | { 79 | return [_attributedString string]; 80 | } 81 | 82 | - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range 83 | { 84 | return [_attributedString attributesAtIndex:location effectiveRange:range]; 85 | } 86 | 87 | - (void)replaceCharactersInRange:(NSRange)range withString:(NSString*)str 88 | { 89 | [self beginEditing]; 90 | 91 | [_attributedString replaceCharactersInRange:range withString:str]; 92 | 93 | [self edited:NSTextStorageEditedCharacters | NSTextStorageEditedAttributes range:range changeInLength:str.length - range.length]; 94 | [self endEditing]; 95 | } 96 | 97 | - (void)setAttributes:(NSDictionary*)attrs range:(NSRange)range 98 | { 99 | [self beginEditing]; 100 | 101 | [_attributedString setAttributes:attrs range:range]; 102 | 103 | [self edited:NSTextStorageEditedAttributes range:range changeInLength:0]; 104 | [self endEditing]; 105 | } 106 | 107 | -(void)processEditing 108 | { 109 | [self performReplacementsForRange:[self editedRange]]; 110 | [super processEditing]; 111 | } 112 | 113 | - (void)performReplacementsForRange:(NSRange)changedRange 114 | { 115 | NSRange extendedRange = NSUnionRange(changedRange, [[_attributedString string] lineRangeForRange:NSMakeRange(NSMaxRange(changedRange), 0)]); 116 | 117 | [self applyStylesToRange:extendedRange]; 118 | } 119 | 120 | 121 | -(void)update 122 | { 123 | [self addAttributes:@{NSFontAttributeName : self.defaultFont} range:NSMakeRange(0, self.length)]; 124 | 125 | [self applyStylesToRange:NSMakeRange(0, self.length)]; 126 | } 127 | 128 | - (void)applyStylesToRange:(NSRange)searchRange 129 | { 130 | if (self.editedRange.location == NSNotFound) 131 | { 132 | return; 133 | } 134 | 135 | NSRange paragaphRange = [self.string paragraphRangeForRange: self.editedRange]; 136 | 137 | // Reset the text attributes 138 | [self setAttributes:@{NSForegroundColorAttributeName : [UIColor blackColor]} range:paragaphRange]; 139 | 140 | [self setAttributes:@{NSFontAttributeName : self.defaultFont} range:paragaphRange]; 141 | 142 | for (CYRToken *attribute in self.tokens) 143 | { 144 | NSRegularExpression *regex = [self expressionForDefinition:attribute.name]; 145 | 146 | [regex enumerateMatchesInString:self.string options:0 range:paragaphRange 147 | usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 148 | 149 | [attribute.attributes enumerateKeysAndObjectsUsingBlock:^(NSString *attributeName, id attributeValue, BOOL *stop) { 150 | [self addAttribute:attributeName value:attributeValue range:result.range]; 151 | }]; 152 | }]; 153 | } 154 | } 155 | 156 | - (NSRegularExpression *)expressionForDefinition:(NSString *)definition 157 | { 158 | __block CYRToken *attribute = nil; 159 | 160 | [self.tokens enumerateObjectsUsingBlock:^(CYRToken *enumeratedAttribute, NSUInteger idx, BOOL *stop) { 161 | if ([enumeratedAttribute.name isEqualToString:definition]) 162 | { 163 | attribute = enumeratedAttribute; 164 | *stop = YES; 165 | } 166 | }]; 167 | 168 | NSRegularExpression *expression = self.regularExpressionCache[attribute.expression]; 169 | 170 | if (!expression) 171 | { 172 | expression = [NSRegularExpression regularExpressionWithPattern:attribute.expression 173 | options:NSRegularExpressionCaseInsensitive error:nil]; 174 | 175 | [self.regularExpressionCache setObject:expression forKey:definition]; 176 | } 177 | 178 | return expression; 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextView.h 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // Copyright (c) 2013 Dominik Hauser 9 | // Copyright (c) 2013 Sam Rijs 10 | // 11 | // Distributed under MIT license. 12 | // Get the latest version from here: 13 | // 14 | // https://github.com/illyabusigin/CYRTextView 15 | // Gestures sourced from: https://github.com/srijs/NLTextView 16 | // 17 | // The MIT License (MIT) 18 | // 19 | // Copyright (c) 2014 Cyrillian, Inc. 20 | // 21 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 22 | // this software and associated documentation files (the "Software"), to deal in 23 | // the Software without restriction, including without limitation the rights to 24 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 25 | // the Software, and to permit persons to whom the Software is furnished to do so, 26 | // subject to the following conditions: 27 | // 28 | // The above copyright notice and this permission notice shall be included in all 29 | // copies or substantial portions of the Software. 30 | // 31 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 33 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 34 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 35 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 36 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | 38 | #import 39 | #import "CYRToken.h" 40 | 41 | @interface CYRTextView : UITextView 42 | 43 | @property (nonatomic, strong) NSArray *tokens; 44 | @property (nonatomic, strong) UIPanGestureRecognizer *singleFingerPanRecognizer; 45 | @property (nonatomic, strong) UIPanGestureRecognizer *doubleFingerPanRecognizer; 46 | 47 | @property UIColor *gutterBackgroundColor; 48 | @property UIColor *gutterLineColor; 49 | 50 | @property (nonatomic, assign) BOOL lineCursorEnabled; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextView.m 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // Copyright (c) 2013 Dominik Hauser 9 | // Copyright (c) 2013 Sam Rijs 10 | // 11 | // Distributed under MIT license. 12 | // Get the latest version from here: 13 | // 14 | // https://github.com/illyabusigin/CYRTextView 15 | // 16 | // The MIT License (MIT) 17 | // 18 | // Copyright (c) 2014 Cyrillian, Inc. 19 | // 20 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 21 | // this software and associated documentation files (the "Software"), to deal in 22 | // the Software without restriction, including without limitation the rights to 23 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 24 | // the Software, and to permit persons to whom the Software is furnished to do so, 25 | // subject to the following conditions: 26 | // 27 | // The above copyright notice and this permission notice shall be included in all 28 | // copies or substantial portions of the Software. 29 | // 30 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 32 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 33 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 34 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 35 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | #import "CYRTextView.h" 38 | #import "CYRLayoutManager.h" 39 | #import "CYRTextStorage.h" 40 | 41 | #define RGB(r,g,b) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:1.0f] 42 | 43 | static void *CYRTextViewContext = &CYRTextViewContext; 44 | static const float kCursorVelocity = 1.0f/8.0f; 45 | 46 | @interface CYRTextView () 47 | 48 | @property (nonatomic, strong) CYRLayoutManager *lineNumberLayoutManager; 49 | @property (nonatomic, strong) CYRTextStorage *syntaxTextStorage; 50 | 51 | @end 52 | 53 | @implementation CYRTextView 54 | { 55 | NSRange startRange; 56 | } 57 | 58 | #pragma mark - Initialization & Setup 59 | 60 | - (id)initWithFrame:(CGRect)frame 61 | { 62 | CYRTextStorage *textStorage = [CYRTextStorage new]; 63 | CYRLayoutManager *layoutManager = [CYRLayoutManager new]; 64 | 65 | self.lineNumberLayoutManager = layoutManager; 66 | 67 | NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; 68 | 69 | // Wrap text to the text view's frame 70 | textContainer.widthTracksTextView = YES; 71 | 72 | [layoutManager addTextContainer:textContainer]; 73 | 74 | [textStorage removeLayoutManager:textStorage.layoutManagers.firstObject]; 75 | [textStorage addLayoutManager:layoutManager]; 76 | 77 | self.syntaxTextStorage = textStorage; 78 | 79 | if ((self = [super initWithFrame:frame textContainer:textContainer])) 80 | { 81 | self.contentMode = UIViewContentModeRedraw; // causes drawRect: to be called on frame resizing and device rotation 82 | 83 | [self _commonSetup]; 84 | } 85 | 86 | return self; 87 | } 88 | 89 | - (void)_commonSetup 90 | { 91 | // Setup observers 92 | [self addObserver:self forKeyPath:NSStringFromSelector(@selector(font)) options:NSKeyValueObservingOptionNew context:CYRTextViewContext]; 93 | [self addObserver:self forKeyPath:NSStringFromSelector(@selector(selectedTextRange)) options:NSKeyValueObservingOptionNew context:CYRTextViewContext]; 94 | [self addObserver:self forKeyPath:NSStringFromSelector(@selector(selectedRange)) options:NSKeyValueObservingOptionNew context:CYRTextViewContext]; 95 | 96 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self]; 97 | 98 | // Setup defaults 99 | self.font = [UIFont systemFontOfSize:16.0f]; 100 | self.autocapitalizationType = UITextAutocapitalizationTypeNone; 101 | self.autocorrectionType = UITextAutocorrectionTypeNo; 102 | self.lineCursorEnabled = YES; 103 | self.gutterBackgroundColor = [UIColor colorWithWhite:0.95 alpha:1]; 104 | self.gutterLineColor = [UIColor lightGrayColor]; 105 | 106 | // Inset the content to make room for line numbers 107 | self.textContainerInset = UIEdgeInsetsMake(8, self.lineNumberLayoutManager.gutterWidth, 8, 0); 108 | 109 | // Setup the gesture recognizers 110 | _singleFingerPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(singleFingerPanHappend:)]; 111 | _singleFingerPanRecognizer.maximumNumberOfTouches = 1; 112 | [self addGestureRecognizer:_singleFingerPanRecognizer]; 113 | 114 | _doubleFingerPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doubleFingerPanHappend:)]; 115 | _doubleFingerPanRecognizer.minimumNumberOfTouches = 2; 116 | [self addGestureRecognizer:_doubleFingerPanRecognizer]; 117 | } 118 | 119 | 120 | #pragma mark - Cleanup 121 | 122 | - (void)dealloc 123 | { 124 | [self removeObserver:self forKeyPath:NSStringFromSelector(@selector(font))]; 125 | [self removeObserver:self forKeyPath:NSStringFromSelector(@selector(selectedTextRange))]; 126 | [self removeObserver:self forKeyPath:NSStringFromSelector(@selector(selectedRange))]; 127 | } 128 | 129 | 130 | #pragma mark - KVO 131 | 132 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 133 | { 134 | if ([keyPath isEqualToString:NSStringFromSelector(@selector(font))] && context == CYRTextViewContext) 135 | { 136 | // Whenever the UITextView font is changed we want to keep a reference in the stickyFont ivar. We do this to counteract a bug where the underlying font can be changed without notice and cause undesired behaviour. 137 | self.syntaxTextStorage.defaultFont = self.font; 138 | } 139 | else if (([keyPath isEqualToString:NSStringFromSelector(@selector(selectedTextRange))] || 140 | [keyPath isEqualToString:NSStringFromSelector(@selector(selectedRange))]) && context == CYRTextViewContext) 141 | { 142 | [self setNeedsDisplay]; 143 | } 144 | else 145 | { 146 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 147 | } 148 | } 149 | 150 | 151 | #pragma mark - Notifications 152 | 153 | - (void)handleTextViewDidChangeNotification:(NSNotification *)notification 154 | { 155 | if (notification.object == self) 156 | { 157 | CGRect line = [self caretRectForPosition: self.selectedTextRange.start]; 158 | CGFloat overflow = line.origin.y + line.size.height - ( self.contentOffset.y + self.bounds.size.height - self.contentInset.bottom - self.contentInset.top ); 159 | 160 | if ( overflow > 0 ) 161 | { 162 | // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it) 163 | // Scroll caret to visible area 164 | CGPoint offset = self.contentOffset; 165 | offset.y += overflow + 7; // leave 7 pixels margin 166 | // Cannot animate with setContentOffset:animated: or caret will not appear 167 | // [UIView animateWithDuration:.2 animations:^{ 168 | // [self setContentOffset:offset]; 169 | // }]; 170 | } 171 | } 172 | } 173 | 174 | 175 | #pragma mark - Overrides 176 | 177 | - (void)setTokens:(NSMutableArray *)tokens 178 | { 179 | [self.syntaxTextStorage setTokens:tokens]; 180 | } 181 | 182 | - (NSArray *)tokens 183 | { 184 | CYRTextStorage *syntaxTextStorage = (CYRTextStorage *)self.textStorage; 185 | 186 | return syntaxTextStorage.tokens; 187 | } 188 | 189 | - (void)setText:(NSString *)text 190 | { 191 | UITextRange *textRange = [self textRangeFromPosition:self.beginningOfDocument toPosition:self.endOfDocument]; 192 | [self replaceRange:textRange withText:text]; 193 | } 194 | 195 | 196 | #pragma mark - Line Drawing 197 | 198 | // Original implementation sourced from: https://github.com/alldritt/TextKit_LineNumbers 199 | - (void)drawRect:(CGRect)rect 200 | { 201 | // Drag the line number gutter background. The line numbers them selves are drawn by LineNumberLayoutManager. 202 | CGContextRef context = UIGraphicsGetCurrentContext(); 203 | CGRect bounds = self.bounds; 204 | 205 | CGFloat height = MAX(CGRectGetHeight(bounds), self.contentSize.height) + 200; 206 | 207 | // Set the regular fill 208 | CGContextSetFillColorWithColor(context, self.gutterBackgroundColor.CGColor); 209 | CGContextFillRect(context, CGRectMake(bounds.origin.x, bounds.origin.y, self.lineNumberLayoutManager.gutterWidth, height)); 210 | 211 | // Draw line 212 | CGContextSetFillColorWithColor(context, self.gutterLineColor.CGColor); 213 | CGContextFillRect(context, CGRectMake(self.lineNumberLayoutManager.gutterWidth, bounds.origin.y, 0.5, height)); 214 | 215 | if (_lineCursorEnabled) 216 | { 217 | self.lineNumberLayoutManager.selectedRange = self.selectedRange; 218 | 219 | NSRange glyphRange = [self.lineNumberLayoutManager.textStorage.string paragraphRangeForRange:self.selectedRange]; 220 | glyphRange = [self.lineNumberLayoutManager glyphRangeForCharacterRange:glyphRange actualCharacterRange:NULL]; 221 | self.lineNumberLayoutManager.selectedRange = glyphRange; 222 | [self.lineNumberLayoutManager invalidateDisplayForGlyphRange:glyphRange]; 223 | } 224 | 225 | [super drawRect:rect]; 226 | } 227 | 228 | 229 | #pragma mark - Gestures 230 | 231 | // Sourced from: https://github.com/srijs/NLTextView 232 | - (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer 233 | { 234 | // Only accept horizontal pans for the code navigation to preserve correct scrolling behaviour. 235 | if (gestureRecognizer == _singleFingerPanRecognizer || gestureRecognizer == _doubleFingerPanRecognizer) 236 | { 237 | CGPoint translation = [gestureRecognizer translationInView:self]; 238 | return fabsf(translation.x) > fabsf(translation.y); 239 | } 240 | 241 | return YES; 242 | 243 | } 244 | 245 | // Sourced from: https://github.com/srijs/NLTextView 246 | - (void)singleFingerPanHappend:(UIPanGestureRecognizer *)sender 247 | { 248 | if (sender.state == UIGestureRecognizerStateBegan) 249 | { 250 | startRange = self.selectedRange; 251 | } 252 | 253 | CGFloat cursorLocation = MAX(startRange.location + [sender translationInView:self].x * kCursorVelocity, 0); 254 | 255 | self.selectedRange = NSMakeRange(cursorLocation, 0); 256 | } 257 | 258 | // Sourced from: https://github.com/srijs/NLTextView 259 | - (void)doubleFingerPanHappend:(UIPanGestureRecognizer *)sender 260 | { 261 | if (sender.state == UIGestureRecognizerStateBegan) 262 | { 263 | startRange = self.selectedRange; 264 | } 265 | 266 | CGFloat cursorLocation = MAX(startRange.location + [sender translationInView:self].x * kCursorVelocity, 0); 267 | 268 | if (cursorLocation > startRange.location) 269 | { 270 | self.selectedRange = NSMakeRange(startRange.location, fabsf(startRange.location - cursorLocation)); 271 | } 272 | else 273 | { 274 | self.selectedRange = NSMakeRange(cursorLocation, fabsf(startRange.location - cursorLocation)); 275 | } 276 | } 277 | 278 | @end 279 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRToken.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextAttribute.h 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // 14 | // The MIT License (MIT) 15 | // 16 | // Copyright (c) 2014 Cyrillian, Inc. 17 | // 18 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | // this software and associated documentation files (the "Software"), to deal in 20 | // the Software without restriction, including without limitation the rights to 21 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 22 | // the Software, and to permit persons to whom the Software is furnished to do so, 23 | // subject to the following conditions: 24 | // 25 | // The above copyright notice and this permission notice shall be included in all 26 | // copies or substantial portions of the Software. 27 | // 28 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 30 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 31 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 32 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | #import 36 | 37 | @interface CYRToken : NSObject 38 | 39 | @property (nonatomic, strong) NSString *name; 40 | @property (nonatomic, strong) NSString *expression; 41 | @property (nonatomic, strong) NSDictionary *attributes; 42 | 43 | + (instancetype)tokenWithName:(NSString *)name expression:(NSString *)expression attributes:(NSDictionary *)attributes; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NodelikeDemo/CYRToken.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYRTextAttribute.m 3 | // 4 | // Version 0.2.0 5 | // 6 | // Created by Illya Busigin on 01/05/2014. 7 | // Copyright (c) 2014 Cyrillian, Inc. 8 | // 9 | // Distributed under MIT license. 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/illyabusigin/CYRTextView 13 | // 14 | // The MIT License (MIT) 15 | // 16 | // Copyright (c) 2014 Cyrillian, Inc. 17 | // 18 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | // this software and associated documentation files (the "Software"), to deal in 20 | // the Software without restriction, including without limitation the rights to 21 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 22 | // the Software, and to permit persons to whom the Software is furnished to do so, 23 | // subject to the following conditions: 24 | // 25 | // The above copyright notice and this permission notice shall be included in all 26 | // copies or substantial portions of the Software. 27 | // 28 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 30 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 31 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 32 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | #import "CYRToken.h" 36 | 37 | @implementation CYRToken 38 | 39 | + (instancetype)tokenWithName:(NSString *)name expression:(NSString *)expression attributes:(NSDictionary *)attributes 40 | { 41 | CYRToken *textAttribute = [CYRToken new]; 42 | 43 | textAttribute.name = name; 44 | textAttribute.expression = expression; 45 | textAttribute.attributes = attributes; 46 | 47 | return textAttribute; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /NodelikeDemo/FHSegmentedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // KPSegmentedViewController.h 3 | // KPKuaiPai 4 | // 5 | // Created by Johnny iDay on 13-12-14. 6 | // Copyright (c) 2013年 Johnny iDay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FHSegmentedViewController : UIViewController 12 | 13 | @property(nonatomic, assign) UIViewController *selectedViewController; 14 | @property(nonatomic, strong) IBOutlet UISegmentedControl *segmentedControl; 15 | @property(nonatomic, assign) NSInteger selectedViewControllerIndex; 16 | 17 | - (void)setViewControllers:(NSArray *)viewControllers; 18 | - (void)setViewControllers:(NSArray *)viewControllers titles:(NSArray *)titles; 19 | - (void)setViewControllers:(NSArray *)viewControllers imagesNamed:(NSArray *)imageNames; 20 | - (void)setViewControllers:(NSArray *)viewControllers images:(NSArray *)images; 21 | - (void)pushViewController:(UIViewController *)viewController; 22 | - (void)pushViewController:(UIViewController *)viewController title:(NSString *)title; 23 | - (void)pushViewController:(UIViewController *)viewController imageNamed:(NSString *)imageName; 24 | - (void)pushViewController:(UIViewController *)viewController image:(UIImage *)image; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /NodelikeDemo/FHSegmentedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // KPSegmentedViewController.m 3 | // KPKuaiPai 4 | // 5 | // Created by Johnny iDay on 13-12-14. 6 | // Copyright (c) 2013年 Johnny iDay. All rights reserved. 7 | // 8 | 9 | #import "FHSegmentedViewController.h" 10 | 11 | @interface FHSegmentedViewController () 12 | 13 | @end 14 | 15 | @implementation FHSegmentedViewController 16 | 17 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 18 | { 19 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 20 | if (self) { 21 | // Custom initialization 22 | } 23 | return self; 24 | } 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | // Do any additional setup after loading the view. 30 | 31 | if (!_segmentedControl) { 32 | _segmentedControl = [[UISegmentedControl alloc] init]; 33 | _segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar; 34 | self.navigationItem.titleView = _segmentedControl; 35 | } else { 36 | [_segmentedControl removeAllSegments]; 37 | } 38 | [_segmentedControl addTarget:self action:@selector(segmentedControlSelected:) forControlEvents:UIControlEventValueChanged]; 39 | } 40 | 41 | - (void)didReceiveMemoryWarning 42 | { 43 | [super didReceiveMemoryWarning]; 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | - (void)setViewControllers:(NSArray *)viewControllers titles:(NSArray *)titles 48 | { 49 | if ([_segmentedControl numberOfSegments] > 0) { 50 | return; 51 | } 52 | for (int i = 0; i < [viewControllers count]; i++) { 53 | [self pushViewController:viewControllers[i] title:titles[i]]; 54 | } 55 | [_segmentedControl setSelectedSegmentIndex:0]; 56 | self.selectedViewControllerIndex = 0; 57 | } 58 | 59 | - (void)setViewControllers:(NSArray *)viewControllers imagesNamed:(NSArray *)imageNames { 60 | if ([_segmentedControl numberOfSegments] > 0) { 61 | return; 62 | } 63 | for (int i = 0; i < [viewControllers count]; i++) { 64 | [self pushViewController:viewControllers[i] imageNamed:imageNames[i]]; 65 | } 66 | [_segmentedControl setSelectedSegmentIndex:0]; 67 | self.selectedViewControllerIndex = 0; 68 | } 69 | 70 | - (void)setViewControllers:(NSArray *)viewControllers images:(NSArray *)images { 71 | if ([_segmentedControl numberOfSegments] > 0) { 72 | return; 73 | } 74 | for (int i = 0; i < [viewControllers count]; i++) { 75 | [self pushViewController:viewControllers[i] image:images[i]]; 76 | } 77 | [_segmentedControl setSelectedSegmentIndex:0]; 78 | self.selectedViewControllerIndex = 0; 79 | } 80 | 81 | - (void)setViewControllers:(NSArray *)viewControllers 82 | { 83 | if ([_segmentedControl numberOfSegments] > 0) { 84 | return; 85 | } 86 | for (int i = 0; i < [viewControllers count]; i++) { 87 | [self pushViewController:viewControllers[i] title:[viewControllers[i] title]]; 88 | } 89 | [_segmentedControl setSelectedSegmentIndex:0]; 90 | self.selectedViewControllerIndex = 0; 91 | } 92 | 93 | - (void)pushViewController:(UIViewController *)viewController 94 | { 95 | [self pushViewController:viewController title:viewController.title]; 96 | } 97 | - (void)pushViewController:(UIViewController *)viewController title:(NSString *)title 98 | { 99 | [_segmentedControl insertSegmentWithTitle:title atIndex:_segmentedControl.numberOfSegments animated:NO]; 100 | [self addChildViewController:viewController]; 101 | [_segmentedControl sizeToFit]; 102 | } 103 | 104 | - (void)pushViewController:(UIViewController *)viewController imageNamed:(NSString *)imageName 105 | { 106 | [_segmentedControl insertSegmentWithImage:[UIImage imageNamed:imageName] atIndex:_segmentedControl.numberOfSegments animated:NO]; 107 | [self addChildViewController:viewController]; 108 | [_segmentedControl sizeToFit]; 109 | } 110 | 111 | - (void)pushViewController:(UIViewController *)viewController image:(UIImage *)image { 112 | [_segmentedControl insertSegmentWithImage:image atIndex:_segmentedControl.numberOfSegments animated:NO]; 113 | [self addChildViewController:viewController]; 114 | [_segmentedControl sizeToFit]; 115 | } 116 | 117 | - (void)segmentedControlSelected:(id)sender 118 | { 119 | self.selectedViewControllerIndex = _segmentedControl.selectedSegmentIndex; 120 | } 121 | 122 | - (void)setSelectedViewControllerIndex:(NSInteger)index 123 | { 124 | if (!_selectedViewController) { 125 | _selectedViewController = self.childViewControllers[index]; 126 | if ([[UIDevice currentDevice].systemVersion floatValue] < 7.0f) { 127 | CGFloat deltaTop = 20.0f; 128 | if (self.navigationController && !self.navigationController.navigationBar.translucent) { 129 | deltaTop = self.navigationController.navigationBar.frame.size.height; 130 | } 131 | CGRect frame = self.view.frame; 132 | [_selectedViewController view].frame = CGRectMake(frame.origin.x, frame.origin.y - deltaTop, frame.size.width, frame.size.height); 133 | // [[_selectedViewController view] sizeToFit]; 134 | } else { 135 | [_selectedViewController view].frame = self.view.frame; 136 | } 137 | [self.view addSubview:[_selectedViewController view]]; 138 | [_selectedViewController didMoveToParentViewController:self]; 139 | } else if (index != _selectedViewControllerIndex) { 140 | if ([[UIDevice currentDevice].systemVersion floatValue] < 7.0f) { 141 | [self.childViewControllers[index] view].frame = self.view.frame; 142 | } 143 | [self transitionFromViewController:_selectedViewController toViewController:self.childViewControllers[index] duration:0.0f options:UIViewAnimationOptionTransitionNone animations:nil completion:^(BOOL finished) { 144 | _selectedViewController = self.childViewControllers[index]; 145 | _selectedViewControllerIndex = index; 146 | }]; 147 | } 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Small@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Spotlight-40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "idiom" : "iphone", 23 | "size" : "57x57", 24 | "scale" : "1x" 25 | }, 26 | { 27 | "idiom" : "iphone", 28 | "size" : "57x57", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "60x60", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-60@2x.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "size" : "29x29", 39 | "idiom" : "ipad", 40 | "filename" : "Icon-Small-1.png", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "size" : "29x29", 45 | "idiom" : "ipad", 46 | "filename" : "Icon-Small@2x-1.png", 47 | "scale" : "2x" 48 | }, 49 | { 50 | "size" : "40x40", 51 | "idiom" : "ipad", 52 | "filename" : "Icon-Spotlight-40.png", 53 | "scale" : "1x" 54 | }, 55 | { 56 | "size" : "40x40", 57 | "idiom" : "ipad", 58 | "filename" : "Icon-Spotlight-40@2x-1.png", 59 | "scale" : "2x" 60 | }, 61 | { 62 | "idiom" : "ipad", 63 | "size" : "50x50", 64 | "scale" : "1x" 65 | }, 66 | { 67 | "idiom" : "ipad", 68 | "size" : "50x50", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "idiom" : "ipad", 73 | "size" : "72x72", 74 | "scale" : "1x" 75 | }, 76 | { 77 | "idiom" : "ipad", 78 | "size" : "72x72", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "76x76", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-76.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-76@2x.png", 91 | "scale" : "2x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-black.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "hal-black.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "hal-black@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-black.imageset/hal-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-black.imageset/hal-black.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-black.imageset/hal-black@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-black.imageset/hal-black@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-blue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "hal-blue.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "hal-blue@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-blue.imageset/hal-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-blue.imageset/hal-blue.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-blue.imageset/hal-blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-blue.imageset/hal-blue@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "hal-white.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "hal-white@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-white.imageset/hal-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-white.imageset/hal-white.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/KOKeyboard/hal-white.imageset/hal-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/KOKeyboard/hal-white.imageset/hal-white@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "launch-phone@2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch-phone-r4-1.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "filename" : "launch-pad.png", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "orientation" : "landscape", 30 | "idiom" : "ipad", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "filename" : "launch-pad-land.png", 34 | "scale" : "1x" 35 | }, 36 | { 37 | "orientation" : "portrait", 38 | "idiom" : "ipad", 39 | "extent" : "full-screen", 40 | "minimum-system-version" : "7.0", 41 | "filename" : "launch-pad@2x.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "filename" : "launch-pad-land@2x.png", 50 | "scale" : "2x" 51 | }, 52 | { 53 | "orientation" : "portrait", 54 | "idiom" : "iphone", 55 | "extent" : "full-screen", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "orientation" : "portrait", 60 | "idiom" : "iphone", 61 | "extent" : "full-screen", 62 | "scale" : "2x" 63 | }, 64 | { 65 | "orientation" : "portrait", 66 | "idiom" : "iphone", 67 | "extent" : "full-screen", 68 | "subtype" : "retina4", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "orientation" : "portrait", 73 | "idiom" : "ipad", 74 | "extent" : "to-status-bar", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "orientation" : "portrait", 79 | "idiom" : "ipad", 80 | "extent" : "full-screen", 81 | "scale" : "1x" 82 | }, 83 | { 84 | "orientation" : "landscape", 85 | "idiom" : "ipad", 86 | "extent" : "to-status-bar", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "orientation" : "landscape", 91 | "idiom" : "ipad", 92 | "extent" : "full-screen", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "orientation" : "portrait", 97 | "idiom" : "ipad", 98 | "extent" : "to-status-bar", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "orientation" : "portrait", 103 | "idiom" : "ipad", 104 | "extent" : "full-screen", 105 | "scale" : "2x" 106 | }, 107 | { 108 | "orientation" : "landscape", 109 | "idiom" : "ipad", 110 | "extent" : "to-status-bar", 111 | "scale" : "2x" 112 | }, 113 | { 114 | "orientation" : "landscape", 115 | "idiom" : "ipad", 116 | "extent" : "full-screen", 117 | "scale" : "2x" 118 | } 119 | ], 120 | "info" : { 121 | "version" : 1, 122 | "author" : "xcode" 123 | } 124 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad-land.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad-land.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad-land@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad-land@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-pad@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-phone-r4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-phone-r4-1.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-phone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/LaunchImage.launchimage/launch-phone@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/bookmarks.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "bookmarks.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "bookmarks@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/bookmarks.imageset/bookmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/bookmarks.imageset/bookmarks.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/bookmarks.imageset/bookmarks@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/bookmarks.imageset/bookmarks@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/info.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "info.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "info@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/info.imageset/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/info.imageset/info.png -------------------------------------------------------------------------------- /NodelikeDemo/Images.xcassets/info.imageset/info@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/NodelikeDemo/Images.xcassets/info.imageset/info@2x.png -------------------------------------------------------------------------------- /NodelikeDemo/Interpreter-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleDocumentTypes 10 | 11 | 12 | CFBundleTypeName 13 | JavaScript 14 | CFBundleTypeRole 15 | Viewer 16 | LSHandlerRank 17 | Default 18 | LSItemContentTypes 19 | 20 | public.text 21 | 22 | 23 | 24 | CFBundleExecutable 25 | ${EXECUTABLE_NAME} 26 | CFBundleIdentifier 27 | de.awesam.${PRODUCT_NAME:rfc1034identifier} 28 | CFBundleInfoDictionaryVersion 29 | 6.0 30 | CFBundleName 31 | ${PRODUCT_NAME} 32 | CFBundlePackageType 33 | APPL 34 | CFBundleShortVersionString 35 | 1.2.0 36 | CFBundleSignature 37 | ???? 38 | CFBundleVersion 39 | 145036003 40 | LSRequiresIPhoneOS 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIMainStoryboardFile~ipad 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UIStatusBarStyle 53 | UIStatusBarStyleBlackOpaque 54 | UISupportedInterfaceOrientations 55 | 56 | UIInterfaceOrientationPortrait 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationLandscapeLeft 62 | UIInterfaceOrientationLandscapeRight 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /NodelikeDemo/Interpreter-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif -------------------------------------------------------------------------------- /NodelikeDemo/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /NodelikeDemo/NLAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLAppDelegate.h 3 | // NodelikeDemo 4 | // 5 | // Created by Sam Rijs on 10/13/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | 13 | @interface NLAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NodelikeDemo/NLAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLAppDelegate.m 3 | // NodelikeDemo 4 | // 5 | // Created by Sam Rijs on 10/13/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "NLAppDelegate.h" 10 | 11 | #import "NLContext.h" 12 | 13 | @implementation NLAppDelegate { 14 | NSString *scriptToLoad; 15 | } 16 | 17 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { 18 | 19 | NSString *script = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; 20 | 21 | if (script) { 22 | 23 | scriptToLoad = script; 24 | 25 | UIAlertView *alert = [[UIAlertView alloc] 26 | initWithTitle:@"Open File" 27 | message:@"You are about to load a file into the editor. This will clear your current script." 28 | delegate:self 29 | cancelButtonTitle:@"Cancel" 30 | otherButtonTitles:@"OK", 31 | nil]; 32 | [alert show]; 33 | 34 | } 35 | 36 | return YES; 37 | } 38 | 39 | - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 40 | if ([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"OK"]) { 41 | [NSNotificationCenter.defaultCenter postNotificationName:@"NLFileOpen" object:nil userInfo:@{@"script": scriptToLoad}]; 42 | } 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NodelikeDemo/NLColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+NLColor.h 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 11/20/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NLColor : UIColor 12 | 13 | + (UIColor *)greenColor; 14 | + (UIColor *)blackColor; 15 | + (UIColor *)beigeColor; 16 | + (UIColor *)whiteColor; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /NodelikeDemo/NLColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+NLColor.m 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 11/20/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "NLColor.h" 10 | 11 | @implementation NLColor 12 | 13 | + (UIColor *)greenColor { return [self colorWithRed:163/255. green:232/255. blue: 54/255. alpha:1]; } 14 | + (UIColor *)blackColor { return [self colorWithRed: 34/255. green: 34/255. blue: 34/255. alpha:1]; } 15 | + (UIColor *)beigeColor { return [self colorWithRed:252/255. green:245/255. blue:228/255. alpha:1]; } 16 | + (UIColor *)whiteColor { return [self colorWithRed:236/255. green:235/255. blue:232/255. alpha:1]; } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /NodelikeDemo/NLConsoleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLConsoleViewController.h 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 1/28/14. 6 | // Copyright (c) 2014 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "NLMasterViewController.h" 12 | 13 | @interface NLConsoleViewController : UIViewController 14 | 15 | @property NLMasterViewController *masterViewController; 16 | 17 | @property IBOutlet UITextView *console; 18 | 19 | @property NSString *consoleText; 20 | 21 | - (void)log:(NSString *)string; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /NodelikeDemo/NLConsoleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLConsoleViewController.m 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 1/28/14. 6 | // Copyright (c) 2014 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "NLConsoleViewController.h" 10 | 11 | @implementation NLConsoleViewController 12 | 13 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 14 | self = [super initWithCoder:aDecoder]; 15 | self.consoleText = @""; 16 | return self; 17 | } 18 | 19 | - (void)viewDidAppear:(BOOL)animated { 20 | self.console.text = self.consoleText; 21 | } 22 | 23 | - (void)log:(NSString *)string { 24 | dispatch_async(dispatch_get_main_queue(), ^{ 25 | self.consoleText = [self.consoleText stringByAppendingFormat:@"%@\n", string]; 26 | self.console.text = self.consoleText; 27 | }); 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /NodelikeDemo/NLDocumentationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLDocumentationViewController.h 3 | // Pinbrowser 4 | // 5 | // Created by Mikael Konutgan on 11/02/2013. 6 | // Copyright (c) 2013 Mikael Konutgan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NLDocumentationViewController : UIViewController 12 | 13 | @property (strong, nonatomic) IBOutlet UIWebView *webView; 14 | 15 | /** 16 | * The URL that will be loaded by the web view controller. 17 | * If there is one present when the web view appears, it will be automatically loaded, by calling `load`, 18 | * Otherwise, you can set a `URL` after the web view has already been loaded and then manually call `load`. 19 | */ 20 | @property (strong, nonatomic) NSURL *URL; 21 | 22 | /** The array of data objects on which to perform the activity. */ 23 | @property (strong, nonatomic) NSArray *activityItems; 24 | 25 | /** An array of UIActivity objects representing the custom services that your application supports. */ 26 | @property (strong, nonatomic) NSArray *applicationActivities; 27 | 28 | /** The list of services that should not be displayed. */ 29 | @property (strong, nonatomic) NSArray *excludedActivityTypes; 30 | 31 | /** 32 | * A Boolean indicating whether the web view controller’s toolbar, 33 | * which displays a stop/refresh, back, forward and share button, is shown. 34 | * The default value of this property is `YES`. 35 | */ 36 | @property (assign, nonatomic) BOOL showsNavigationToolbar; 37 | 38 | /** 39 | * Loads the given `URL`. This is called automatically when the when the web view appears if a `URL` exists, 40 | * otehrwise it can be called manually. 41 | */ 42 | - (void)load; 43 | 44 | /** 45 | * Clears the contents of the web view. 46 | */ 47 | - (void)clear; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /NodelikeDemo/NLDocumentationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLDocumentationViewController.m 3 | // Pinbrowser 4 | // 5 | // Created by Mikael Konutgan on 11/02/2013. 6 | // Copyright (c) 2013 Mikael Konutgan. All rights reserved. 7 | // 8 | 9 | #import "NLDocumentationViewController.h" 10 | 11 | @interface NLDocumentationViewController () 12 | 13 | @property (strong, nonatomic) UIBarButtonItem *stopLoadingButton; 14 | @property (strong, nonatomic) UIBarButtonItem *reloadButton; 15 | @property (strong, nonatomic) UIBarButtonItem *backButton; 16 | @property (strong, nonatomic) UIBarButtonItem *forwardButton; 17 | 18 | @property (strong, nonatomic) UIPopoverController *activitiyPopoverController; 19 | 20 | @property (assign, nonatomic) BOOL toolbarPreviouslyHidden; 21 | 22 | @end 23 | 24 | @implementation NLDocumentationViewController 25 | 26 | - (id)initWithCoder:(NSCoder *)aDecoder 27 | { 28 | self = [super initWithCoder:aDecoder]; 29 | if (self) { 30 | _showsNavigationToolbar = NO; 31 | _URL = [NSURL URLWithString:@"http://nodejs.org/docs/latest/api/"]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)load 37 | { 38 | NSURLRequest *request = [NSURLRequest requestWithURL:self.URL]; 39 | [self.webView loadRequest:request]; 40 | 41 | if (self.navigationController.toolbarHidden) { 42 | self.toolbarPreviouslyHidden = YES; 43 | if (self.showsNavigationToolbar) { 44 | [self.navigationController setToolbarHidden:NO animated:YES]; 45 | } 46 | } 47 | } 48 | 49 | - (void)clear 50 | { 51 | [self.webView loadHTMLString:@"" baseURL:nil]; 52 | self.title = @""; 53 | } 54 | 55 | #pragma mark - View controller lifecycle 56 | 57 | - (void)viewDidLoad 58 | { 59 | [super viewDidLoad]; 60 | self.webView.scalesPageToFit = YES; 61 | } 62 | 63 | - (void)viewWillAppear:(BOOL)animated 64 | { 65 | [super viewWillAppear:animated]; 66 | [self setupToolBarItems]; 67 | self.webView.delegate = self; 68 | if (self.URL) { 69 | [self load]; 70 | } 71 | } 72 | 73 | - (void)viewWillDisappear:(BOOL)animated 74 | { 75 | [super viewWillDisappear:animated]; 76 | [self.webView stopLoading]; 77 | self.webView.delegate = nil; 78 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 79 | 80 | if (self.toolbarPreviouslyHidden && self.showsNavigationToolbar) { 81 | [self.navigationController setToolbarHidden:YES animated:YES]; 82 | } 83 | } 84 | 85 | #pragma mark - Helpers 86 | 87 | - (UIImage *)backButtonImage 88 | { 89 | static UIImage *image; 90 | 91 | static dispatch_once_t predicate; 92 | dispatch_once(&predicate, ^{ 93 | CGSize size = CGSizeMake(12.0, 21.0); 94 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); 95 | 96 | UIBezierPath *path = [UIBezierPath bezierPath]; 97 | path.lineWidth = 1.5; 98 | path.lineCapStyle = kCGLineCapButt; 99 | path.lineJoinStyle = kCGLineJoinMiter; 100 | [path moveToPoint:CGPointMake(11.0, 1.0)]; 101 | [path addLineToPoint:CGPointMake(1.0, 11.0)]; 102 | [path addLineToPoint:CGPointMake(11.0, 20.0)]; 103 | [path stroke]; 104 | 105 | image = UIGraphicsGetImageFromCurrentImageContext(); 106 | UIGraphicsEndImageContext(); 107 | }); 108 | 109 | return image; 110 | } 111 | 112 | - (UIImage *)forwardButtonImage 113 | { 114 | static UIImage *image; 115 | 116 | static dispatch_once_t predicate; 117 | dispatch_once(&predicate, ^{ 118 | UIImage *backButtonImage = [self backButtonImage]; 119 | 120 | CGSize size = backButtonImage.size; 121 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); 122 | 123 | CGContextRef context = UIGraphicsGetCurrentContext(); 124 | 125 | CGFloat x_mid = size.width / 2.0; 126 | CGFloat y_mid = size.height / 2.0; 127 | 128 | CGContextTranslateCTM(context, x_mid, y_mid); 129 | CGContextRotateCTM(context, M_PI); 130 | 131 | [backButtonImage drawAtPoint:CGPointMake(-x_mid, -y_mid)]; 132 | 133 | image = UIGraphicsGetImageFromCurrentImageContext(); 134 | UIGraphicsEndImageContext(); 135 | }); 136 | 137 | return image; 138 | } 139 | 140 | - (void)setupToolBarItems 141 | { 142 | 143 | self.stopLoadingButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop 144 | target:self.webView 145 | action:@selector(stopLoading)]; 146 | 147 | self.reloadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 148 | target:self.webView 149 | action:@selector(reload)]; 150 | 151 | self.backButton = [[UIBarButtonItem alloc] initWithImage:[self backButtonImage] 152 | style:UIBarButtonItemStylePlain 153 | target:self.webView 154 | action:@selector(goBack)]; 155 | 156 | self.forwardButton = [[UIBarButtonItem alloc] initWithImage:[self forwardButtonImage] 157 | style:UIBarButtonItemStylePlain 158 | target:self.webView 159 | action:@selector(goForward)]; 160 | 161 | self.backButton.enabled = NO; 162 | self.forwardButton.enabled = NO; 163 | 164 | UIBarButtonItem *actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction 165 | target:self 166 | action:@selector(action:)]; 167 | 168 | UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace 169 | target:nil 170 | action:nil]; 171 | 172 | UIBarButtonItem *space_ = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace 173 | target:nil 174 | action:nil]; 175 | space_.width = 60.0f; 176 | 177 | self.navigationController.toolbarItems = @[self.stopLoadingButton, space, self.backButton, space_, self.forwardButton, space, actionButton]; 178 | } 179 | 180 | - (void)toggleState 181 | { 182 | self.backButton.enabled = self.webView.canGoBack; 183 | self.forwardButton.enabled = self.webView.canGoForward; 184 | 185 | NSMutableArray *toolbarItems = [self.navigationController.toolbarItems mutableCopy]; 186 | if (self.webView.loading) { 187 | toolbarItems[0] = self.stopLoadingButton; 188 | } else { 189 | toolbarItems[0] = self.reloadButton; 190 | } 191 | self.navigationController.toolbarItems = [toolbarItems copy]; 192 | } 193 | 194 | - (void)finishLoad 195 | { 196 | [self toggleState]; 197 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 198 | } 199 | 200 | #pragma mark - Button actions 201 | 202 | - (void)action:(id)sender 203 | { 204 | if (self.activitiyPopoverController.popoverVisible) { 205 | [self.activitiyPopoverController dismissPopoverAnimated:YES]; 206 | return; 207 | } 208 | 209 | NSArray *activityItems; 210 | if (self.activityItems) { 211 | activityItems = [self.activityItems arrayByAddingObject:self.URL]; 212 | } else { 213 | activityItems = @[self.URL]; 214 | } 215 | 216 | UIActivityViewController *vc = [[UIActivityViewController alloc] initWithActivityItems:activityItems 217 | applicationActivities:self.applicationActivities]; 218 | if (self.excludedActivityTypes) { 219 | vc.excludedActivityTypes = self.excludedActivityTypes; 220 | } 221 | 222 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 223 | [self presentViewController:vc animated:YES completion:NULL]; 224 | } else { 225 | if (!self.activitiyPopoverController) { 226 | self.activitiyPopoverController = [[UIPopoverController alloc] initWithContentViewController:vc]; 227 | } 228 | self.activitiyPopoverController.delegate = self; 229 | [self.activitiyPopoverController presentPopoverFromBarButtonItem:[self.toolbarItems lastObject] 230 | permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; 231 | } 232 | } 233 | 234 | #pragma mark - Web view delegate 235 | 236 | - (void)webViewDidStartLoad:(UIWebView *)webView 237 | { 238 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 239 | [self toggleState]; 240 | } 241 | 242 | - (void)webViewDidFinishLoad:(UIWebView *)webView 243 | { 244 | [self finishLoad]; 245 | self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 246 | self.URL = self.webView.request.URL; 247 | } 248 | 249 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 250 | { 251 | [self finishLoad]; 252 | } 253 | 254 | #pragma mark - Popover controller delegate 255 | 256 | - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController 257 | { 258 | self.activitiyPopoverController = nil; 259 | } 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /NodelikeDemo/NLEditorViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLEditorViewController.h 3 | // NodelikeDemo 4 | // 5 | // Created by Sam Rijs on 10/13/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NLMasterViewController.h" 11 | 12 | #import "NLTextView.h" 13 | 14 | @interface NLEditorViewController : UIViewController 15 | 16 | @property IBOutlet NLTextView *input; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /NodelikeDemo/NLEditorViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLEditorViewController.m 3 | // NodelikeDemo 4 | // 5 | // Created by Sam Rijs on 10/13/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "NLEditorViewController.h" 10 | 11 | #import "KOKeyboardRow.h" 12 | 13 | #import "NLColor.h" 14 | #import "NLContext.h" 15 | 16 | @implementation NLEditorViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | 22 | self.input = [NLTextView textViewForView:self.view]; 23 | 24 | self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 25 | 26 | [self.view addSubview:self.input]; 27 | 28 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 29 | [KOKeyboardRow applyToTextView:self.input]; 30 | ((KOKeyboardRow *)self.input.inputAccessoryView).viewController = self; 31 | } 32 | 33 | [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(fileOpenTriggered:) name:@"NLFileOpen" object:nil]; 34 | 35 | } 36 | 37 | - (void)viewDidAppear:(BOOL)animated { 38 | [self.input becomeFirstResponder]; 39 | } 40 | 41 | - (void)fileOpenTriggered:(NSNotification*)notification { 42 | self.input.text = notification.userInfo[@"script"]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NodelikeDemo/NLMasterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLMasterViewController.h 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 1/28/14. 6 | // Copyright (c) 2014 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "FHSegmentedViewController.h" 10 | 11 | #import "NLContext.h" 12 | 13 | @interface NLMasterViewController : FHSegmentedViewController 14 | 15 | @property UIViewController *editorViewController; 16 | @property UIViewController *consoleViewController; 17 | @property UIViewController *documentationViewController; 18 | 19 | @property NLContext *context; 20 | 21 | @property UIBackgroundTaskIdentifier backgroundTask; 22 | 23 | - (void)executeJS:(NSString *)code; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /NodelikeDemo/NLMasterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLMasterViewController.m 3 | // Interpreter 4 | // 5 | // Created by Sam Rijs on 1/28/14. 6 | // Copyright (c) 2014 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import "NLMasterViewController.h" 10 | 11 | #import "NLEditorViewController.h" 12 | #import "NLConsoleViewController.h" 13 | 14 | #import "CSNotificationView.h" 15 | #import "PBWebViewController.h" 16 | 17 | #import "NLColor.h" 18 | 19 | @interface NLMasterViewController () 20 | 21 | @end 22 | 23 | @implementation NLMasterViewController 24 | 25 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 26 | { 27 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 28 | if (self) { 29 | // Custom initialization 30 | } 31 | return self; 32 | } 33 | 34 | - (void)viewDidLoad 35 | { 36 | 37 | [super viewDidLoad]; 38 | 39 | self.editorViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"editorViewController"]; 40 | self.consoleViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"consoleViewController"]; 41 | self.documentationViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"documentationViewController"]; 42 | 43 | [self setViewControllers:@[self.editorViewController, self.consoleViewController]]; 44 | [self pushViewController:self.documentationViewController]; 45 | 46 | [self setupStyle]; 47 | 48 | __weak NLMasterViewController *weakSelf = self; 49 | 50 | _context = [[NLContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]]; 51 | 52 | [NLContext attachToContext:_context]; 53 | 54 | _context.exceptionHandler = ^(JSContext *c, JSValue *e) { 55 | dispatch_async(dispatch_get_main_queue(), ^{ 56 | [weakSelf error:[e toString]]; 57 | NSLog(@"%@ stack: %@", e, [e valueForProperty:@"stack"]); 58 | }); 59 | }; 60 | 61 | id logger = ^(JSValue *thing) { 62 | [JSContext.currentArguments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 63 | NSLog(@"log: %@", [obj toString]); 64 | [((NLConsoleViewController *)self.consoleViewController) log:[obj toString]]; 65 | }]; 66 | }; 67 | _context[@"console"] = @{@"log": logger, @"error": logger}; 68 | 69 | //[_context evaluateScript:@"process.env['NODE_DEBUG']='module'"]; 70 | 71 | } 72 | 73 | - (void)setupStyle { 74 | 75 | //self.navigationController.navigationBar.tintColor = [NLColor greenColor]; 76 | //self.navigationController.navigationBar.barTintColor = [NLColor blackColor]; 77 | self.navigationController.toolbar.tintColor = [NLColor blackColor]; 78 | self.navigationController.toolbar.barTintColor = [[NLColor whiteColor] colorWithAlphaComponent:0.5]; 79 | 80 | } 81 | 82 | - (void)executeJS:(NSString *)code { 83 | JSValue *ret = [_context evaluateScript:code]; 84 | if (![ret isUndefined]) { 85 | [self output:[ret toString]]; 86 | } 87 | 88 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 89 | 90 | self.backgroundTask = [UIApplication.sharedApplication beginBackgroundTaskWithExpirationHandler:^{ 91 | NSLog(@"beginBG called"); 92 | [UIApplication.sharedApplication endBackgroundTask:self.backgroundTask]; 93 | self.backgroundTask = UIBackgroundTaskInvalid; 94 | }]; 95 | 96 | [NLContext runEventLoopSync]; 97 | 98 | [UIApplication.sharedApplication endBackgroundTask:self.backgroundTask]; 99 | self.backgroundTask = UIBackgroundTaskInvalid; 100 | 101 | }); 102 | 103 | } 104 | 105 | - (void)output:(NSString *)message { 106 | [CSNotificationView showInViewController:self 107 | style:CSNotificationViewStyleSuccess 108 | message:message]; 109 | } 110 | 111 | - (void)error:(NSString *)message { 112 | [CSNotificationView showInViewController:self 113 | style:CSNotificationViewStyleError 114 | message:message]; 115 | } 116 | 117 | - (IBAction)execute:(id)sender { 118 | [self executeJS:((NLEditorViewController *)self.editorViewController).input.text]; 119 | } 120 | 121 | - (IBAction)showInfo:(id)sender { 122 | PBWebViewController *docuViewController = [[PBWebViewController alloc] init]; 123 | docuViewController.URL = [NSURL URLWithString:@"http://nodeapp.org/?utm_source=interpreter&utm_medium=App&utm_campaign=info"]; 124 | [self.navigationController pushViewController:docuViewController animated:YES]; 125 | } 126 | 127 | 128 | - (void)didReceiveMemoryWarning 129 | { 130 | [super didReceiveMemoryWarning]; 131 | // Dispose of any resources that can be recreated. 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /NodelikeDemo/NLTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2013 Dominik Hauser 5 | // Copyright (c) 2013 Sam Rijs 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | // this software and associated documentation files (the "Software"), to deal in 9 | // the Software without restriction, including without limitation the rights to 10 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | // the Software, and to permit persons to whom the Software is furnished to do so, 12 | // subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | #import "CYRTextView.h" 28 | 29 | @interface NLTextView : CYRTextView 30 | 31 | + (instancetype)textViewForView:(UIView *)view; 32 | 33 | @property NSDictionary *highlightDefinition; 34 | @property (nonatomic) NSDictionary *highlightTheme; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /NodelikeDemo/NLTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2013 Dominik Hauser 5 | // Copyright (c) 2013 Sam Rijs 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | // this software and associated documentation files (the "Software"), to deal in 9 | // the Software without restriction, including without limitation the rights to 10 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | // the Software, and to permit persons to whom the Software is furnished to do so, 12 | // subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | // 24 | 25 | #import "NLTextView.h" 26 | 27 | #import "CYRToken.h" 28 | 29 | @implementation NLTextView 30 | 31 | + (instancetype)textViewForView:(UIView *)view { 32 | CGRect frame = view.frame; 33 | frame.size.height -= 64; 34 | return [[self alloc] initWithFrame:frame]; 35 | } 36 | 37 | - (instancetype)initWithFrame:(CGRect)frame { 38 | self = [super initWithFrame:frame]; 39 | 40 | self.gutterBackgroundColor = [UIColor clearColor]; 41 | self.gutterLineColor = [UIColor clearColor]; 42 | 43 | self.font = [UIFont fontWithName:@"Menlo" size:14.0]; 44 | 45 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; 46 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 47 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil]; 48 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; 49 | 50 | self.highlightDefinition = [NLTextView defaultHighlightDefinition]; 51 | self.highlightTheme = [NLTextView defaultHighlightTheme]; 52 | 53 | NSMutableArray *tokens = [NSMutableArray new]; 54 | [self.highlightDefinition enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 55 | [tokens addObject:[CYRToken tokenWithName:key expression:obj 56 | attributes:@{NSForegroundColorAttributeName:self.highlightTheme[key]}]]; 57 | }]; 58 | self.tokens = tokens; 59 | 60 | return self; 61 | } 62 | 63 | 64 | - (void)keyboardWillShow:(NSNotification *)notification { 65 | [self moveTextViewForKeyboard:notification up:YES]; 66 | } 67 | 68 | - (void)keyboardWillHide:(NSNotification *)notification { 69 | [self moveTextViewForKeyboard:notification up:NO]; 70 | } 71 | 72 | - (void)keyboardDidChangeFrame:(NSNotification *)notification { 73 | CGRect keyboardEndFrame, newFrame, keyboardFrame; 74 | [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; 75 | newFrame = self.superview.bounds; 76 | keyboardFrame = [self.superview convertRect:keyboardEndFrame toView:nil]; 77 | newFrame.size.height -= keyboardFrame.size.height; 78 | self.frame = newFrame; 79 | } 80 | 81 | - (void)orientationChanged:(NSNotification *)notification { 82 | self.frame = self.superview.bounds; 83 | } 84 | 85 | - (void)moveTextViewForKeyboard:(NSNotification* )notification up:(BOOL)up { 86 | NSDictionary* userInfo = [notification userInfo]; 87 | NSTimeInterval animationDuration; 88 | UIViewAnimationCurve animationCurve; 89 | CGRect keyboardEndFrame; 90 | 91 | [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; 92 | [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; 93 | [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; 94 | 95 | [UIView beginAnimations:nil context:nil]; 96 | [UIView setAnimationDuration:animationDuration]; 97 | [UIView setAnimationCurve:animationCurve]; 98 | 99 | CGRect newFrame = self.frame; 100 | CGRect keyboardFrame = [self.superview convertRect:keyboardEndFrame toView:nil]; 101 | newFrame.size.height -= keyboardFrame.size.height * (up?1:-1); 102 | self.frame = newFrame; 103 | 104 | [UIView commitAnimations]; 105 | } 106 | 107 | + (NSDictionary *)defaultHighlightDefinition { 108 | 109 | NSString *path = [NSBundle.mainBundle pathForResource:@"Syntax" ofType:@"plist"]; 110 | return [NSDictionary dictionaryWithContentsOfFile:path]; 111 | 112 | } 113 | 114 | + (NSDictionary *)defaultHighlightTheme { 115 | 116 | return @{//@"text": [UIColor colorWithRed:255.0/255 green:255.0/255 blue:255.0/255 alpha:1], 117 | //@"background": [UIColor colorWithRed: 40.0/255 green: 43.0/255 blue: 52.0/255 alpha:1], 118 | @"comment": [UIColor colorWithRed: 72.0/255 green:190.0/255 blue:102.0/255 alpha:1], 119 | @"documentation_comment": [UIColor colorWithRed: 72.0/255 green:190.0/255 blue:102.0/255 alpha:1], 120 | @"documentation_comment_keyword": [UIColor colorWithRed: 72.0/255 green:190.0/255 blue:102.0/255 alpha:1], 121 | @"string": [UIColor colorWithRed:230.0/255 green: 66.0/255 blue: 75.0/255 alpha:1], 122 | @"character": [UIColor colorWithRed:139.0/255 green:134.0/255 blue:201.0/255 alpha:1], 123 | @"number": [UIColor colorWithRed:139.0/255 green:134.0/255 blue:201.0/255 alpha:1], 124 | @"keyword": [UIColor colorWithRed:195.0/255 green: 55.0/255 blue:149.0/255 alpha:1], 125 | @"preprocessor": [UIColor colorWithRed:211.0/255 green:142.0/255 blue: 99.0/255 alpha:1], 126 | @"url": [UIColor colorWithRed: 35.0/255 green: 63.0/255 blue:208.0/255 alpha:1], 127 | @"attribute": [UIColor colorWithRed:103.0/255 green:135.0/255 blue:142.0/255 alpha:1], 128 | @"project": [UIColor colorWithRed:146.0/255 green:199.0/255 blue:119.0/255 alpha:1], 129 | @"other": [UIColor colorWithRed: 0.0/255 green:175.0/255 blue:199.0/255 alpha:1]}; 130 | 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /NodelikeDemo/PBWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PBWebViewController.h 3 | // Pinbrowser 4 | // 5 | // Created by Mikael Konutgan on 11/02/2013. 6 | // Copyright (c) 2013 Mikael Konutgan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PBWebViewController : UIViewController 12 | 13 | /** 14 | * The URL that will be loaded by the web view controller. 15 | * If there is one present when the web view appears, it will be automatically loaded, by calling `load`, 16 | * Otherwise, you can set a `URL` after the web view has already been loaded and then manually call `load`. 17 | */ 18 | @property (strong, nonatomic) NSURL *URL; 19 | 20 | /** The array of data objects on which to perform the activity. */ 21 | @property (strong, nonatomic) NSArray *activityItems; 22 | 23 | /** An array of UIActivity objects representing the custom services that your application supports. */ 24 | @property (strong, nonatomic) NSArray *applicationActivities; 25 | 26 | /** The list of services that should not be displayed. */ 27 | @property (strong, nonatomic) NSArray *excludedActivityTypes; 28 | 29 | /** 30 | * A Boolean indicating whether the web view controller’s toolbar, 31 | * which displays a stop/refresh, back, forward and share button, is shown. 32 | * The default value of this property is `YES`. 33 | */ 34 | @property (assign, nonatomic) BOOL showsNavigationToolbar; 35 | 36 | /** 37 | * Loads the given `URL`. This is called automatically when the when the web view appears if a `URL` exists, 38 | * otehrwise it can be called manually. 39 | */ 40 | - (void)load; 41 | 42 | /** 43 | * Clears the contents of the web view. 44 | */ 45 | - (void)clear; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /NodelikeDemo/PBWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PBWebViewController.m 3 | // Pinbrowser 4 | // 5 | // Created by Mikael Konutgan on 11/02/2013. 6 | // Copyright (c) 2013 Mikael Konutgan. All rights reserved. 7 | // 8 | 9 | #import "PBWebViewController.h" 10 | 11 | @interface PBWebViewController () 12 | 13 | @property (strong, nonatomic) UIWebView *webView; 14 | 15 | @property (strong, nonatomic) UIBarButtonItem *stopLoadingButton; 16 | @property (strong, nonatomic) UIBarButtonItem *reloadButton; 17 | @property (strong, nonatomic) UIBarButtonItem *backButton; 18 | @property (strong, nonatomic) UIBarButtonItem *forwardButton; 19 | 20 | @property (strong, nonatomic) UIPopoverController *activitiyPopoverController; 21 | 22 | @property (assign, nonatomic) BOOL toolbarPreviouslyHidden; 23 | 24 | @end 25 | 26 | @implementation PBWebViewController 27 | 28 | - (id)init 29 | { 30 | self = [super init]; 31 | if (self) { 32 | _showsNavigationToolbar = YES; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)load 38 | { 39 | NSURLRequest *request = [NSURLRequest requestWithURL:self.URL]; 40 | [self.webView loadRequest:request]; 41 | 42 | if (self.navigationController.toolbarHidden) { 43 | self.toolbarPreviouslyHidden = YES; 44 | if (self.showsNavigationToolbar) { 45 | [self.navigationController setToolbarHidden:NO animated:YES]; 46 | } 47 | } 48 | } 49 | 50 | - (void)clear 51 | { 52 | [self.webView loadHTMLString:@"" baseURL:nil]; 53 | self.title = @""; 54 | } 55 | 56 | #pragma mark - View controller lifecycle 57 | 58 | - (void)loadView 59 | { 60 | self.webView = [[UIWebView alloc] init]; 61 | self.webView.scalesPageToFit = YES; 62 | self.view = self.webView; 63 | } 64 | 65 | - (void)viewDidLoad 66 | { 67 | [super viewDidLoad]; 68 | [self setupToolBarItems]; 69 | } 70 | 71 | - (void)viewWillAppear:(BOOL)animated 72 | { 73 | [super viewWillAppear:animated]; 74 | self.webView.delegate = self; 75 | if (self.URL) { 76 | [self load]; 77 | } 78 | } 79 | 80 | - (void)viewWillDisappear:(BOOL)animated 81 | { 82 | [super viewWillDisappear:animated]; 83 | [self.webView stopLoading]; 84 | self.webView.delegate = nil; 85 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 86 | 87 | if (self.toolbarPreviouslyHidden && self.showsNavigationToolbar) { 88 | [self.navigationController setToolbarHidden:YES animated:YES]; 89 | } 90 | } 91 | 92 | #pragma mark - Helpers 93 | 94 | - (UIImage *)backButtonImage 95 | { 96 | static UIImage *image; 97 | 98 | static dispatch_once_t predicate; 99 | dispatch_once(&predicate, ^{ 100 | CGSize size = CGSizeMake(12.0, 21.0); 101 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); 102 | 103 | UIBezierPath *path = [UIBezierPath bezierPath]; 104 | path.lineWidth = 1.5; 105 | path.lineCapStyle = kCGLineCapButt; 106 | path.lineJoinStyle = kCGLineJoinMiter; 107 | [path moveToPoint:CGPointMake(11.0, 1.0)]; 108 | [path addLineToPoint:CGPointMake(1.0, 11.0)]; 109 | [path addLineToPoint:CGPointMake(11.0, 20.0)]; 110 | [path stroke]; 111 | 112 | image = UIGraphicsGetImageFromCurrentImageContext(); 113 | UIGraphicsEndImageContext(); 114 | }); 115 | 116 | return image; 117 | } 118 | 119 | - (UIImage *)forwardButtonImage 120 | { 121 | static UIImage *image; 122 | 123 | static dispatch_once_t predicate; 124 | dispatch_once(&predicate, ^{ 125 | UIImage *backButtonImage = [self backButtonImage]; 126 | 127 | CGSize size = backButtonImage.size; 128 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); 129 | 130 | CGContextRef context = UIGraphicsGetCurrentContext(); 131 | 132 | CGFloat x_mid = size.width / 2.0; 133 | CGFloat y_mid = size.height / 2.0; 134 | 135 | CGContextTranslateCTM(context, x_mid, y_mid); 136 | CGContextRotateCTM(context, M_PI); 137 | 138 | [backButtonImage drawAtPoint:CGPointMake(-x_mid, -y_mid)]; 139 | 140 | image = UIGraphicsGetImageFromCurrentImageContext(); 141 | UIGraphicsEndImageContext(); 142 | }); 143 | 144 | return image; 145 | } 146 | 147 | - (void)setupToolBarItems 148 | { 149 | self.stopLoadingButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop 150 | target:self.webView 151 | action:@selector(stopLoading)]; 152 | 153 | self.reloadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 154 | target:self.webView 155 | action:@selector(reload)]; 156 | 157 | self.backButton = [[UIBarButtonItem alloc] initWithImage:[self backButtonImage] 158 | style:UIBarButtonItemStylePlain 159 | target:self.webView 160 | action:@selector(goBack)]; 161 | 162 | self.forwardButton = [[UIBarButtonItem alloc] initWithImage:[self forwardButtonImage] 163 | style:UIBarButtonItemStylePlain 164 | target:self.webView 165 | action:@selector(goForward)]; 166 | 167 | self.backButton.enabled = NO; 168 | self.forwardButton.enabled = NO; 169 | 170 | UIBarButtonItem *actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction 171 | target:self 172 | action:@selector(action:)]; 173 | 174 | UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace 175 | target:nil 176 | action:nil]; 177 | 178 | UIBarButtonItem *space_ = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace 179 | target:nil 180 | action:nil]; 181 | space_.width = 60.0f; 182 | 183 | self.toolbarItems = @[self.stopLoadingButton, space, self.backButton, space_, self.forwardButton, space, actionButton]; 184 | } 185 | 186 | - (void)toggleState 187 | { 188 | self.backButton.enabled = self.webView.canGoBack; 189 | self.forwardButton.enabled = self.webView.canGoForward; 190 | 191 | NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy]; 192 | if (self.webView.loading) { 193 | toolbarItems[0] = self.stopLoadingButton; 194 | } else { 195 | toolbarItems[0] = self.reloadButton; 196 | } 197 | self.toolbarItems = [toolbarItems copy]; 198 | } 199 | 200 | - (void)finishLoad 201 | { 202 | [self toggleState]; 203 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 204 | } 205 | 206 | #pragma mark - Button actions 207 | 208 | - (void)action:(id)sender 209 | { 210 | if (self.activitiyPopoverController.popoverVisible) { 211 | [self.activitiyPopoverController dismissPopoverAnimated:YES]; 212 | return; 213 | } 214 | 215 | NSArray *activityItems; 216 | if (self.activityItems) { 217 | activityItems = [self.activityItems arrayByAddingObject:self.URL]; 218 | } else { 219 | activityItems = @[self.URL]; 220 | } 221 | 222 | UIActivityViewController *vc = [[UIActivityViewController alloc] initWithActivityItems:activityItems 223 | applicationActivities:self.applicationActivities]; 224 | if (self.excludedActivityTypes) { 225 | vc.excludedActivityTypes = self.excludedActivityTypes; 226 | } 227 | 228 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 229 | [self presentViewController:vc animated:YES completion:NULL]; 230 | } else { 231 | if (!self.activitiyPopoverController) { 232 | self.activitiyPopoverController = [[UIPopoverController alloc] initWithContentViewController:vc]; 233 | } 234 | self.activitiyPopoverController.delegate = self; 235 | [self.activitiyPopoverController presentPopoverFromBarButtonItem:[self.toolbarItems lastObject] 236 | permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; 237 | } 238 | } 239 | 240 | #pragma mark - Web view delegate 241 | 242 | - (void)webViewDidStartLoad:(UIWebView *)webView 243 | { 244 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 245 | [self toggleState]; 246 | } 247 | 248 | - (void)webViewDidFinishLoad:(UIWebView *)webView 249 | { 250 | [self finishLoad]; 251 | self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 252 | self.URL = self.webView.request.URL; 253 | } 254 | 255 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 256 | { 257 | [self finishLoad]; 258 | } 259 | 260 | #pragma mark - Popover controller delegate 261 | 262 | - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController 263 | { 264 | self.activitiyPopoverController = nil; 265 | } 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /NodelikeDemo/SEJSONViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SEJSONViewController.h 3 | // SEJSONViewController 4 | // 5 | // Created by Sérgio Estêvão on 04/09/2013. 6 | // Copyright (c) 2013 Sérgio Estêvão. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | SEJSONViewController allows to browse the content of a JSON object. 13 | 14 | @warning SEJSONViewControllers must be used with a navigation controller in order to function properly and to be able to drill down trough the JSON data. 15 | */ 16 | @interface SEJSONViewController : UITableViewController 17 | 18 | - (void) setData:(id)data; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /NodelikeDemo/SEJSONViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SEJSONViewController.m 3 | // SEJSONViewController 4 | // 5 | // Created by Sérgio Estêvão on 04/09/2013. 6 | // Copyright (c) 2013 Sérgio Estêvão. All rights reserved. 7 | // 8 | 9 | #import "SEJSONViewController.h" 10 | 11 | @interface SEJSONViewController () { 12 | id _data; 13 | } 14 | 15 | @end 16 | 17 | @implementation SEJSONViewController 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 | 33 | - (void)didReceiveMemoryWarning 34 | { 35 | [super didReceiveMemoryWarning]; 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | - (void) setData:(id)data { 40 | _data = data; 41 | [self.tableView reloadData]; 42 | } 43 | #pragma mark - Table view data source 44 | 45 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 46 | { 47 | if (_data == nil) return 0; 48 | // Return the number of sections. 49 | if ([_data isKindOfClass:[NSDictionary class]]){ 50 | return 1; 51 | } 52 | 53 | if ([_data isKindOfClass:[NSArray class]]){ 54 | return [_data count]; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 61 | { 62 | // Return the number of rows in the section. 63 | if ([_data isKindOfClass:[NSDictionary class]]){ 64 | return [_data count]; 65 | } 66 | 67 | if ([_data isKindOfClass:[NSArray class]]){ 68 | id obj = _data[section]; 69 | if ([obj isKindOfClass:[NSDictionary class]]){ 70 | return [[obj allKeys] count]; 71 | } 72 | return 1; 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 79 | { 80 | static NSString *CellIdentifier = @"Cell"; 81 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 82 | 83 | // Configure the cell... 84 | if (cell == nil){ 85 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier]; 86 | } 87 | id obj = _data; 88 | if ([_data isKindOfClass:[NSArray class]]){ 89 | obj = _data[indexPath.section]; 90 | } 91 | 92 | if ([obj isKindOfClass:[NSDictionary class]]){ 93 | cell.textLabel.text = [obj allKeys][indexPath.row]; 94 | id subObj = obj[cell.textLabel.text]; 95 | if ([[subObj class] isSubclassOfClass:[NSDictionary class]] || 96 | [[subObj class] isSubclassOfClass:[NSArray class]] 97 | ) 98 | { 99 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 100 | cell.detailTextLabel.text = @""; 101 | } else { 102 | cell.accessoryType = UITableViewCellAccessoryNone; 103 | cell.detailTextLabel.text = [subObj description]; 104 | } 105 | } else if ([obj isKindOfClass:[NSArray class]]) { 106 | cell.textLabel.text = [NSString stringWithFormat:@"%li",(long)indexPath.row]; 107 | cell.detailTextLabel.text = @""; 108 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 109 | } else { 110 | cell.textLabel.text = [obj description]; 111 | cell.detailTextLabel.text = @""; 112 | cell.accessoryType = UITableViewCellAccessoryNone; 113 | } 114 | 115 | 116 | return cell; 117 | } 118 | 119 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 120 | { 121 | if ([[_data class] isSubclassOfClass:[NSArray class]]){ 122 | return [NSString stringWithFormat:@"%li",(long)section]; 123 | } 124 | 125 | return @"Attributes"; 126 | } 127 | 128 | 129 | #pragma mark - Table view delegate 130 | 131 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 132 | { 133 | // Navigation logic may go here. Create and push another view controller. 134 | NSString * title = self.title; 135 | id obj = _data; 136 | id selectedObjet = nil; 137 | if ([_data isKindOfClass:[NSArray class]]){ 138 | obj = _data[indexPath.section]; 139 | } 140 | 141 | if ([obj isKindOfClass:[NSDictionary class]]){ 142 | NSString * key = [obj allKeys][indexPath.row]; 143 | selectedObjet = obj[key]; 144 | title = key; 145 | } else if ([obj isKindOfClass:[NSArray class]]) { 146 | selectedObjet = obj[indexPath.row]; 147 | title = [NSString stringWithFormat:@"%@-%li",self.title, (long)indexPath.row]; 148 | } 149 | 150 | if ( !([selectedObjet isKindOfClass:[NSDictionary class]] || [selectedObjet isKindOfClass:[NSArray class]])) return; 151 | 152 | SEJSONViewController *detailViewController = [[SEJSONViewController alloc] initWithStyle:self.tableView.style]; 153 | [detailViewController setData:selectedObjet]; 154 | detailViewController.title = title; 155 | // ... 156 | // Pass the selected object to the new view controller. 157 | [self.navigationController pushViewController:detailViewController animated:YES]; 158 | 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /NodelikeDemo/Syntax.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | comment 6 | (\/\/[^"\n\r]*(?:"[^"\n\r]*"[^"\n\r]*)*[\r\n]) 7 | documentation_comment 8 | \/\*(.*?)\*\/ 9 | documentation_comment_keyword 10 | (\/\*|\*\/) 11 | keyword 12 | (?<!\w)(if|else|for|in|while|do|continue|break|with|try|catch|switch|case|new|var|function|return|delete|true|false|void|throw|typeof|const|default|escape|isFinite|isNaN|Number|parseFloat|parseInt|reload|taint|unescape|untaint|write|Anchor|Applet|Area|Array|Boolean|Button|Checkbox|Date|document|window|Image|FileUpload|Form|Frame|Function|Hidden|Link|MimeType|Math|Max|Min|Layer|navigator|Object|Password|Plugin|Radio|RegExp|Reset|Screen|Select|String|Text|Textarea|this|Window|abs|acos|asin|atan|atan2|ceil|cos|ctg|E|exp|floor|LN2|LN10|log|LOG2E|LOG10E|PI|pow|round|sin|sqrt|SQRT1_2|SQRT2|tan|onAbort|onBlur|onChange|onClick|onError|onFocus|onLoad|onMouseOut|onMouseOver|onReset|onSelect|onSubmit|onUnload|above|action|alinkColor|alert|anchor|anchors|appCodeName|applets|apply|appName|appVersion|argument|arguments|arity|availHeight|availWidth|back|background|below|bgColor|border|big|blink|blur|bold|border|call|caller|charAt|charCodeAt|checked|clearInterval|clearTimeout|click|clip|close|closed|colorDepth|complete|compile|constructor|confirm|cookie|current|cursor|data|defaultChecked|defaultSelected|defaultStatus|defaultValue|description|disableExternalCapture|domain|elements|embeds|enabledPlugin|enableExternalCapture|encoding|eval|exec|fgColor|filename|find|fixed|focus|fontcolor|fontsize|form|forms|formName|forward|frames|fromCharCode|getDate|getDay|getHours|getMiliseconds|getMinutes|getMonth|getSeconds|getSelection|getTime|getTimezoneOffset|getUTCDate|getUTCDay|getUTCFullYear|getUTCHours|getUTCMilliseconds|getUTCMinutes|getUTCMonth|getUTCSeconds|getYear|global|go|hash|height|history|home|host|hostname|href|hspace|ignoreCase|images|index|indexOf|innerHeight|innerWidth|input|italics|javaEnabled|join|language|lastIndex|lastIndexOf|lastModified|lastParen|layers|layerX|layerY|left|leftContext|length|link|linkColor|links|location|locationbar|load|lowsrc|match|MAX_VALUE|menubar|method|mimeTypes|MIN_VALUE|modifiers|moveAbove|moveBelow|moveBy|moveTo|moveToAbsolute|multiline|name|NaN|NEGATIVE_INFINITY|negative_infinity|next|open|opener|options|outerHeight|outerWidth|pageX|pageY|pageXoffset|pageYoffset|parent|parse|pathname|personalbar|pixelDepth|platform|plugins|pop|port|POSITIVE_INFINITY|positive_infinity|preference|previous|print|prompt|protocol|prototype|push|referrer|refresh|releaseEvents|reload|replace|reset|resizeBy|resizeTo|reverse|rightContext|screenX|screenY|scroll|scrollbar|scrollBy|scrollTo|search|select|selected|selectedIndex|self|setDate|setHours|setMinutes|setMonth|setSeconds|setTime|setTimeout|setUTCDate|setUTCDay|setUTCFullYear|setUTCHours|setUTCMilliseconds|setUTCMinutes|setUTCMonth|setUTCSeconds|setYear|shift|siblingAbove|siblingBelow|small|sort|source|splice|split|src|status|statusbar|strike|sub|submit|substr|substring|suffixes|sup|taintEnabled|target|test|text|title|toGMTString|toLocaleString|toLowerCase|toolbar|toSource|toString|top|toUpperCase|toUTCString|type|URL|unshift|unwatch|userAgent|UTC|value|valueOf|visibility|vlinkColor|vspace|width|watch|which|width|write|writeln|x|y|zIndex)(?!\w) 13 | number 14 | (?<!\w)(((0x[0-9a-fA-F]+)|(([0-9]+\.?[0-9]*|\.[0-9]+)([eE][-+]?[0-9]+)?))[fFlLuU]{0,2})(?!\w) 15 | string 16 | ("(?:[^"\\]|\\.)*") 17 | url 18 | ((https?|mailto|ftp|file)://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?) 19 | 20 | 21 | -------------------------------------------------------------------------------- /NodelikeDemo/Vendor/KOKeyboard/KOKeyboardRow.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExtraKeyboardRow.h 3 | // KeyboardTest 4 | // 5 | // Created by Kuba on 28.06.12. 6 | // Copyright (c) 2012 Adam Horacek, Kuba Brecka 7 | // 8 | // Website: http://www.becomekodiak.com/ 9 | // github: http://github.com/adamhoracek/KOKeyboard 10 | // Twitter: http://twitter.com/becomekodiak 11 | // Mail: adam@becomekodiak.com, kuba@becomekodiak.com 12 | // 13 | // Permission is hereby granted, free of charge, to any person 14 | // obtaining a copy of this software and associated documentation 15 | // files (the "Software"), to deal in the Software without 16 | // restriction, including without limitation the rights to use, 17 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the 19 | // Software is furnished to do so, subject to the following 20 | // conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | // OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | 35 | #import 36 | 37 | #import "NLEditorViewController.h" 38 | 39 | @interface KOKeyboardRow : UIView 40 | 41 | @property NLEditorViewController *viewController; 42 | 43 | + (void)applyToTextView:(UITextView *)textView; 44 | - (void)trackPointMovedX:(int)xdiff Y:(int)ydiff selecting:(BOOL)selecting; 45 | - (void)trackPointStarted; 46 | - (void)execButtonPressed; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /NodelikeDemo/Vendor/KOKeyboard/KOKeyboardRow.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExtraKeyboardRow.m 3 | // KeyboardTest 4 | // 5 | // Created by Kuba on 28.06.12. 6 | // Copyright (c) 2012 Adam Horacek, Kuba Brecka 7 | // 8 | // Website: http://www.becomekodiak.com/ 9 | // github: http://github.com/adamhoracek/KOKeyboard 10 | // Twitter: http://twitter.com/becomekodiak 11 | // Mail: adam@becomekodiak.com, kuba@becomekodiak.com 12 | // 13 | // Permission is hereby granted, free of charge, to any person 14 | // obtaining a copy of this software and associated documentation 15 | // files (the "Software"), to deal in the Software without 16 | // restriction, including without limitation the rights to use, 17 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the 19 | // Software is furnished to do so, subject to the following 20 | // conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | // OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | 35 | #import "KOKeyboardRow.h" 36 | #import "KOSwipeButton.h" 37 | 38 | #import "NLColor.h" 39 | 40 | @interface KOKeyboardRow () 41 | 42 | @property (nonatomic, retain) UITextView *textView; 43 | @property (nonatomic, assign) CGRect startLocation; 44 | 45 | @end 46 | 47 | @implementation KOKeyboardRow 48 | 49 | @synthesize textView, startLocation; 50 | 51 | + (void)applyToTextView:(UITextView *)t 52 | { 53 | int barHeight = 46; 54 | int barWidth = 768; 55 | 56 | int buttonCount = 1; 57 | int buttonHeight = 40; 58 | NSString *keys = @"TTTTT"; 59 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 60 | buttonCount = 11; 61 | barHeight = 72; 62 | buttonHeight = 60; 63 | keys = @"()\"[]{}'<>\\/$´`~^|€£-+=%*◉◉◉◉◉!?#@&_:;,.1203467589TTTTT"; 64 | } 65 | 66 | KOKeyboardRow *v = [[KOKeyboardRow alloc] initWithFrame:CGRectMake(0, 0, barWidth, barHeight)]; 67 | v.backgroundColor = [NLColor blackColor];//[UIColor colorWithRed:240/255. green:240/255. blue:240/255. alpha:1]; 68 | v.autoresizingMask = UIViewAutoresizingFlexibleHeight; 69 | v.textView = t; 70 | 71 | int leftMargin = 3; 72 | int topMargin = 1; 73 | int buttonSpacing = 6; 74 | int buttonWidth = (barWidth - 2 * leftMargin - (buttonCount - 1) * buttonSpacing) / buttonCount; 75 | leftMargin = (barWidth - buttonWidth * buttonCount - buttonSpacing * (buttonCount - 1)) / 2; 76 | 77 | for (int i = 0; i < buttonCount; i++) { 78 | KOSwipeButton *b = [[KOSwipeButton alloc] initWithFrame:CGRectMake(leftMargin + i * (buttonSpacing + buttonWidth), topMargin + (barHeight - buttonHeight) / 2, buttonWidth, buttonHeight)]; 79 | b.keys = [keys substringWithRange:NSMakeRange(i * 5, 5)]; 80 | b.delegate = v; 81 | b.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 82 | [v addSubview:b]; 83 | } 84 | 85 | t.inputAccessoryView = v; 86 | } 87 | 88 | - (void)insertText:(NSString *)text 89 | { 90 | [textView insertText:text]; 91 | } 92 | 93 | - (void)trackPointStarted 94 | { 95 | startLocation = [textView caretRectForPosition:textView.selectedTextRange.start]; 96 | } 97 | 98 | - (void)trackPointMovedX:(int)xdiff Y:(int)ydiff selecting:(BOOL)selecting 99 | { 100 | CGRect loc = startLocation; 101 | 102 | loc.origin.y -= textView.contentOffset.y; 103 | 104 | UITextPosition *p1 = [textView closestPositionToPoint:loc.origin]; 105 | 106 | loc.origin.x -= xdiff; 107 | loc.origin.y -= ydiff; 108 | 109 | UITextPosition *p2 = [textView closestPositionToPoint:loc.origin]; 110 | 111 | if (!selecting) { 112 | p1 = p2; 113 | } 114 | UITextRange *r = [textView textRangeFromPosition:p1 toPosition:p2]; 115 | 116 | textView.selectedTextRange = r; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /NodelikeDemo/Vendor/KOKeyboard/KOSwipeButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeButton.h 3 | // KeyboardTest 4 | // 5 | // Created by Kuba on 28.06.12. 6 | // Copyright (c) 2012 Adam Horacek, Kuba Brecka 7 | // 8 | // Website: http://www.becomekodiak.com/ 9 | // github: http://github.com/adamhoracek/KOKeyboard 10 | // Twitter: http://twitter.com/becomekodiak 11 | // Mail: adam@becomekodiak.com, kuba@becomekodiak.com 12 | // 13 | // Permission is hereby granted, free of charge, to any person 14 | // obtaining a copy of this software and associated documentation 15 | // files (the "Software"), to deal in the Software without 16 | // restriction, including without limitation the rights to use, 17 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the 19 | // Software is furnished to do so, subject to the following 20 | // conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | // OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | 35 | #import 36 | 37 | @interface KOSwipeButton : UIView 38 | 39 | @property (nonatomic, weak) id delegate; 40 | 41 | - (void)setKeys:(NSString *)newKeys; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /NodelikeDemo/Vendor/KOKeyboard/KOSwipeButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeButton.m 3 | // KeyboardTest 4 | // 5 | // Created by Kuba on 28.06.12. 6 | // Copyright (c) 2012 Adam Horacek, Kuba Brecka 7 | // 8 | // Website: http://www.becomekodiak.com/ 9 | // github: http://github.com/adamhoracek/KOKeyboard 10 | // Twitter: http://twitter.com/becomekodiak 11 | // Mail: adam@becomekodiak.com, kuba@becomekodiak.com 12 | // 13 | // Permission is hereby granted, free of charge, to any person 14 | // obtaining a copy of this software and associated documentation 15 | // files (the "Software"), to deal in the Software without 16 | // restriction, including without limitation the rights to use, 17 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the 19 | // Software is furnished to do so, subject to the following 20 | // conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | // OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | 35 | #import "KOSwipeButton.h" 36 | #import "KOKeyboardRow.h" 37 | 38 | #import "NLColor.h" 39 | 40 | @interface KOSwipeButton () 41 | 42 | @property (nonatomic, retain) NSMutableArray *labels; 43 | @property (nonatomic, assign) CGPoint touchBeginPoint; 44 | @property (nonatomic, retain) UILabel *selectedLabel; 45 | @property (nonatomic, retain) UIImageView *bgView; 46 | @property (nonatomic, retain) UIImageView *foregroundView; 47 | @property (nonatomic, assign) BOOL trackPoint; 48 | @property (nonatomic, assign) BOOL tabButton; 49 | @property (nonatomic, retain) NSDate *firstTapDate; 50 | @property (nonatomic, assign) BOOL selecting; 51 | @property (nonatomic, retain) UIImage *blueImage; 52 | @property (nonatomic, retain) UIImage *pressedImage; 53 | @property (nonatomic, retain) UIImage *blueFgImage; 54 | @property (nonatomic, retain) UIImage *pressedFgImage; 55 | 56 | @end 57 | 58 | #define TIME_INTERVAL_FOR_DOUBLE_TAP 0.4 59 | 60 | @implementation KOSwipeButton 61 | 62 | @synthesize labels, touchBeginPoint, selectedLabel, delegate, bgView, trackPoint, tabButton, selecting, firstTapDate, blueImage, pressedImage, foregroundView, blueFgImage, pressedFgImage; 63 | 64 | - (void)setFrame:(CGRect)frame 65 | { 66 | [super setFrame:frame]; 67 | } 68 | 69 | - (id)initWithFrame:(CGRect)frame 70 | { 71 | self = [super initWithFrame:frame]; 72 | 73 | int labelWidth = 20; 74 | int labelHeight = 20; 75 | int leftInset = 9; 76 | int rightInset = 9; 77 | int topInset = 3; 78 | int bottomInset = 8; 79 | 80 | self.labels = [[NSMutableArray alloc] init]; 81 | 82 | UIFont *f = [UIFont systemFontOfSize:15]; 83 | 84 | UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(leftInset, topInset, labelWidth, labelHeight)]; 85 | l.textAlignment = NSTextAlignmentLeft; 86 | l.text = @"1"; 87 | l.font = f; 88 | [self addSubview:l]; 89 | [l setTextColor:[UIColor whiteColor]]; 90 | [l setHighlightedTextColor:[NLColor greenColor]]; 91 | l.backgroundColor = [UIColor clearColor]; 92 | [labels addObject:l]; 93 | 94 | l = [[UILabel alloc] initWithFrame:CGRectMake(self.frame.size.width - labelWidth - rightInset, topInset, labelWidth, labelHeight)]; 95 | l.textAlignment = NSTextAlignmentRight; 96 | l.text = @"2"; 97 | l.font = f; 98 | l.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; 99 | [self addSubview:l]; 100 | [l setTextColor:[UIColor whiteColor]]; 101 | [l setHighlightedTextColor:[NLColor greenColor]]; 102 | l.backgroundColor = [UIColor clearColor]; 103 | [labels addObject:l]; 104 | 105 | l = [[UILabel alloc] initWithFrame:CGRectIntegral(CGRectMake((self.frame.size.width - labelWidth - leftInset - rightInset) / 2 + leftInset, (self.frame.size.height - labelHeight - topInset - bottomInset) / 2 + topInset, labelWidth, labelHeight))]; 106 | l.textAlignment = NSTextAlignmentCenter; 107 | l.text = @"3"; 108 | l.font = f; 109 | l.autoresizingMask = UIViewAutoresizingFlexibleWidth; 110 | [self addSubview:l]; 111 | [l setTextColor:[UIColor whiteColor]]; 112 | [l setHighlightedTextColor:[NLColor greenColor]]; 113 | l.backgroundColor = [UIColor clearColor]; 114 | [labels addObject:l]; 115 | 116 | l = [[UILabel alloc] initWithFrame:CGRectMake(leftInset, (self.frame.size.height - labelHeight - bottomInset), labelWidth, labelHeight)]; 117 | l.textAlignment = NSTextAlignmentLeft; 118 | l.text = @"4"; 119 | l.font = f; 120 | [self addSubview:l]; 121 | [l setTextColor:[UIColor whiteColor]]; 122 | [l setHighlightedTextColor:[NLColor greenColor]]; 123 | l.backgroundColor = [UIColor clearColor]; 124 | [labels addObject:l]; 125 | 126 | l = [[UILabel alloc] initWithFrame:CGRectMake(self.frame.size.width - labelWidth - rightInset, (self.frame.size.height - labelHeight - bottomInset), labelWidth, labelHeight)]; 127 | l.textAlignment = NSTextAlignmentRight; 128 | l.text = @"5"; 129 | l.font = f; 130 | l.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; 131 | [self addSubview:l]; 132 | [l setTextColor:[UIColor whiteColor]]; 133 | [l setHighlightedTextColor:[NLColor greenColor]]; 134 | l.backgroundColor = [UIColor clearColor]; 135 | [labels addObject:l]; 136 | 137 | firstTapDate = [[NSDate date] dateByAddingTimeInterval:-1]; 138 | 139 | return self; 140 | } 141 | 142 | - (void)setKeys:(NSString *)newKeys 143 | { 144 | for (int i = 0; i < MIN(newKeys.length, 5); i++) { 145 | [[labels objectAtIndex:i] setText:[newKeys substringWithRange:NSMakeRange(i, 1)]]; 146 | 147 | if ([[newKeys substringToIndex:1] isEqualToString:@"◉"] | 148 | [[newKeys substringToIndex:1] isEqualToString:@"T"]) { 149 | 150 | trackPoint = [[newKeys substringToIndex:1] isEqualToString:@"◉"]; 151 | tabButton = [[newKeys substringToIndex:1] isEqualToString:@"T"]; 152 | 153 | if (i != 2) 154 | [[labels objectAtIndex:i] setHidden:YES]; 155 | else { 156 | if (trackPoint) { 157 | [[labels objectAtIndex:i] setHidden:YES]; 158 | 159 | [[labels objectAtIndex:i] setFont:[UIFont systemFontOfSize:20]]; 160 | blueImage = [UIImage imageNamed:@"key-blue.png"]; 161 | pressedImage = [UIImage imageNamed:@"key-pressed.png"]; 162 | 163 | UIImage *bgImg1 = [UIImage imageNamed:@"hal-black.png"]; 164 | UIImage *bgImg2 = [UIImage imageNamed:@"hal-blue.png"]; 165 | blueFgImage = [UIImage imageNamed:@"hal-white.png"]; 166 | pressedFgImage = bgImg2; 167 | foregroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 19, 19)]; 168 | foregroundView.frame = CGRectMake((int)((self.frame.size.width - foregroundView.frame.size.width) / 2), (int)((self.frame.size.height - foregroundView.frame.size.height) / 2), 19, 19); 169 | [foregroundView setImage:bgImg1]; 170 | [foregroundView setHighlightedImage:bgImg2]; 171 | foregroundView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 172 | [self addSubview:foregroundView]; 173 | 174 | } else { 175 | [[labels objectAtIndex:i] setText:@"TAB"]; 176 | [[labels objectAtIndex:i] setFrame:self.bounds]; 177 | } 178 | } 179 | } 180 | } 181 | } 182 | 183 | - (void)selectLabel:(int)idx 184 | { 185 | selectedLabel = nil; 186 | 187 | for (int i = 0; i < labels.count; i++) { 188 | UILabel *l = [labels objectAtIndex:i]; 189 | l.highlighted = (idx == i); 190 | 191 | if (idx == i) 192 | selectedLabel = l; 193 | } 194 | 195 | bgView.highlighted = selectedLabel != nil; 196 | foregroundView.highlighted = selectedLabel != nil; 197 | } 198 | 199 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 200 | { 201 | UITouch *t = [touches anyObject]; 202 | touchBeginPoint = [t locationInView:self]; 203 | 204 | if (trackPoint) { 205 | if (fabs([firstTapDate timeIntervalSinceNow]) < TIME_INTERVAL_FOR_DOUBLE_TAP) { 206 | bgView.highlightedImage = blueImage; 207 | foregroundView.highlightedImage = blueFgImage; 208 | selecting = YES; 209 | } else { 210 | bgView.highlightedImage = pressedImage; 211 | foregroundView.highlightedImage = pressedFgImage; 212 | selecting = NO; 213 | } 214 | firstTapDate = [NSDate date]; 215 | 216 | [delegate trackPointStarted]; 217 | } 218 | 219 | [self selectLabel:2]; 220 | } 221 | 222 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 223 | { 224 | UITouch *t = [touches anyObject]; 225 | CGPoint touchMovePoint = [t locationInView:self]; 226 | 227 | CGFloat xdiff = touchBeginPoint.x - touchMovePoint.x; 228 | CGFloat ydiff = touchBeginPoint.y - touchMovePoint.y; 229 | CGFloat distance = sqrt(xdiff * xdiff + ydiff * ydiff); 230 | 231 | if (trackPoint) { 232 | [delegate trackPointMovedX:xdiff Y:ydiff selecting:selecting]; 233 | return; 234 | } 235 | 236 | if (distance > 250) { 237 | [self selectLabel:-1]; 238 | } else if (!tabButton && (distance > 20)) { 239 | CGFloat angle = atan2(xdiff, ydiff); 240 | 241 | if (angle >= 0 && angle < M_PI_2) { 242 | [self selectLabel:0]; 243 | } else if (angle >= 0 && angle >= M_PI_2) { 244 | [self selectLabel:3]; 245 | } else if (angle < 0 && angle > -M_PI_2) { 246 | [self selectLabel:1]; 247 | } else if (angle < 0 && angle <= -M_PI_2) { 248 | [self selectLabel:4]; 249 | } 250 | } else { 251 | [self selectLabel:2]; 252 | } 253 | } 254 | 255 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 256 | { 257 | if (selectedLabel != nil) { 258 | if (tabButton) { 259 | [delegate insertText:@"\t"]; 260 | } else if (! trackPoint) { 261 | NSString *textToInsert = selectedLabel.text; 262 | [delegate insertText:textToInsert]; 263 | } 264 | } 265 | 266 | [self selectLabel:-1]; 267 | } 268 | 269 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 270 | { 271 | [self selectLabel:-1]; 272 | } 273 | 274 | @end 275 | -------------------------------------------------------------------------------- /NodelikeDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /NodelikeDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NodelikeDemo 4 | // 5 | // Created by Sam Rijs on 10/13/13. 6 | // Copyright (c) 2013 Sam Rijs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "NLAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([NLAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.0' 2 | 3 | pod 'Nodelike', :git => 'https://github.com/node-app/Nodelike.git', :commit => '9eab9ce1053eb80599cb323222601ce8695486dc', :submodules => true 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Important: This project is no longer maintained. The code is still available, but please don't regard it as anything but a hack. Please do not open new support tickets. 2 | 3 | # Interpreter [![Build Status](https://travis-ci.org/node-app/Interpreter.png?branch=master)](https://travis-ci.org/node-app/Interpreter) [![Gitter chat](https://badges.gitter.im/node-app/Interpreter.png)](https://gitter.im/node-app/Interpreter) 4 | 5 | This is an example project implementing a Node.JS interpreter as an iOS app, utilising the [Nodelike](https://github.com/node-app/Nodelike) framework. 6 | 7 | Nodelike is a project to implement a roughly Node.JS-compatible interface using JavaScriptCore.framework on iOS 7 and OS X Mavericks. 8 | 9 | (JavaScriptCore hasn't been available before iOS 7, and on OS X the project makes extensive use of the newly-updated 10.9-only Objective-C API. Previously on 10.8 there existed only a very low-level and very verbose C API.) 10 | 11 | This is currently in a very incomplete state. It could, however, become usable over the following weeks. 12 | 13 | ![demo time](https://raw.github.com/node-app/Interpreter/master/demo.png) 14 | 15 | The goals 16 | --------- 17 | - to be _drop-in compatible_ with the current nodejs master 18 | - to be _very lightweight_ 19 | - to _reuse javascript code from node_ (/lib) 20 | - to provide the _most minimal binding_ that is possible (via libuv) 21 | - NOT to achieve Node.js performance (this is meant as a client-side, not a server-side application) 22 | - NOT to be backwards-compatible (nodejs cutting edge and newest iOS/OS X required) 23 | 24 | What's working right now 25 | ------------------------ 26 | 27 | - `console.log()` 28 | - `process`: `.argv`, `.env`, `.exit()`, `.nextTick()` 29 | - `require()` for native modules 30 | - `fs` 31 | - `net` 32 | - `http` 33 | - `timers` 34 | - `util` 35 | - `url` 36 | - `events` 37 | - `path` 38 | - `stream` 39 | - `querystring` 40 | - `punycode` 41 | - `assert` 42 | 43 | How to compile 44 | -------------- 45 | 46 | 1. You need to have [CocoaPods](http://cocoapods.org) installed. If you do not have already, run `sudo gem install cocoapods`. 47 | 2. Install the dependencies via `pod install`. 48 | 3. Open `Interpreter.xcworkspace` in Xcode and run! 49 | 50 | How to use the app 51 | ------------------ 52 | 53 | You can enter Javascript code into the TextView and execute that via a tap on the `Execute` button. 54 | After each execution, when the result of the executed script is not undefined, a popover will appear containing that result. 55 | 56 | Have fun! 57 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-app/Interpreter/debb0f3ee1e38e6864070d3a8c423a674aa820db/demo.png -------------------------------------------------------------------------------- /node_modules/examples/index.js: -------------------------------------------------------------------------------- 1 | module.exports = ['01','02','03','04'].map(function (m) { return require('example' + m); }); -------------------------------------------------------------------------------- /node_modules/examples/node_modules/example01/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | 3 | var eventemitter = require('events').EventEmitter, 4 | timers = require('timers'); 5 | 6 | var e = new eventemitter() 7 | 8 | e.on("hello", function () { 9 | console.log("Hello!") 10 | }); 11 | 12 | e.on("world", function () { 13 | console.log("World!") 14 | }); 15 | 16 | timers.setTimeout(function () { 17 | e.emit('hello'); 18 | }, 5000); 19 | 20 | timers.setTimeout(function () { 21 | e.emit('world'); 22 | }, 10000); 23 | 24 | }; -------------------------------------------------------------------------------- /node_modules/examples/node_modules/example02/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | 3 | var fs = require("fs"); 4 | 5 | fs.readdir("/", function (err, dir) { 6 | if (err) return console.log(err); 7 | console.log(dir); 8 | }); 9 | 10 | }; -------------------------------------------------------------------------------- /node_modules/examples/node_modules/example03/index.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | 3 | module.exports = function () { 4 | 5 | http.createServer(function (req, res) { 6 | res.statusCode = 200; 7 | res.write(new Buffer("PONG!")); 8 | res.end(); 9 | }).listen(5000); 10 | 11 | http.get("http://localhost:5000/", function(res) { 12 | console.log("Got response: " + res.statusCode); 13 | res.on("data", function (data) { 14 | console.log("Got data: " + data); 15 | }); 16 | }).on('error', function(e) { 17 | console.log("Got error: " + e.message); 18 | }); 19 | 20 | }; -------------------------------------------------------------------------------- /node_modules/examples/node_modules/example04/index.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | 3 | module.exports = function () { 4 | 5 | var server = net.createServer(function (socket) { 6 | socket.write('Echo server\r\n'); 7 | socket.pipe(socket); 8 | }); 9 | 10 | server.listen(1337); 11 | 12 | }; --------------------------------------------------------------------------------