CPDFLinkViewController.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. //
  2. // CPDFLinkViewController.m
  3. // ComPDFKit_Tools
  4. //
  5. // Copyright © 2014-2024 PDF Technologies, Inc. All Rights Reserved.
  6. //
  7. // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
  8. // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
  9. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
  10. // This notice may not be removed from this file.
  11. //
  12. #import "CPDFLinkViewController.h"
  13. #import "CPDFColorUtils.h"
  14. #import "CAnnotStyle.h"
  15. @interface CPDFLinkViewController ()<UITextFieldDelegate>
  16. @property (nonatomic, strong) CAnnotStyle *annotStyle;
  17. @property (nonatomic, strong) UIScrollView *scrcollView;
  18. @property (nonatomic, strong) UIButton *backBtn;
  19. @property (nonatomic, strong) UILabel *titleLabel;
  20. @property (nonatomic, strong) UISegmentedControl *segmentedControl;
  21. @property (nonatomic, strong) UITextField *pageTextField;
  22. @property (nonatomic, strong) UITextField *emailTextField;
  23. @property (nonatomic, strong) UITextField *urlTextField;
  24. @property (nonatomic, strong) UIButton *saveButton;
  25. @property (nonatomic, assign) CPDFLinkType linkType;
  26. @property (nonatomic, strong) UIView *headerView;
  27. @end
  28. @implementation CPDFLinkViewController
  29. #pragma mark - Initializers
  30. - (instancetype)initWithStyle:(CAnnotStyle *)annotStyle {
  31. if (self = [super init]) {
  32. self.annotStyle = annotStyle;
  33. }
  34. return self;
  35. }
  36. - (void)viewDidLoad {
  37. [super viewDidLoad];
  38. self.view.backgroundColor = [CPDFColorUtils CAnnotationSampleBackgoundColor];
  39. self.headerView = [[UIView alloc] init];
  40. self.headerView.layer.borderColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1].CGColor;
  41. self.headerView.layer.borderWidth = 1.0;
  42. self.headerView.backgroundColor = [CPDFColorUtils CAnnotationPropertyViewControllerBackgoundColor];
  43. [self.view addSubview:self.headerView];
  44. [self initWithView];
  45. if(!self.annotStyle.isSelectAnnot) {
  46. self.linkType = CPDFLinkTypeLink;
  47. self.segmentedControl.selectedSegmentIndex = 0;
  48. } else if([self.annotStyle.annotations.firstObject isKindOfClass:[CPDFButtonWidgetAnnotation class]]){
  49. self.segmentedControl.selectedSegmentIndex = 0;
  50. self.linkType = CPDFLinkTypeLink;
  51. }else{
  52. CPDFLinkAnnotation *link = self.annotStyle.annotations.firstObject;
  53. NSString *url = link.URL;
  54. CPDFDestination *destination = link.destination;
  55. if (url) {
  56. if ([url hasPrefix:@"mailto:"]) {
  57. self.emailTextField.text = [url substringFromIndex:7]?:@"";
  58. self.linkType = CPDFLinkTypeEmail;
  59. self.segmentedControl.selectedSegmentIndex = 2;
  60. } else {
  61. self.urlTextField.text = url?:@"";
  62. self.linkType = CPDFLinkTypeLink;
  63. self.segmentedControl.selectedSegmentIndex = 0;
  64. }
  65. } else if (destination) {
  66. self.linkType = CPDFLinkTypePage;
  67. self.pageTextField.text = [NSString stringWithFormat:@"%@", @(destination.pageIndex+1)];
  68. self.segmentedControl.selectedSegmentIndex = 1;
  69. } else {
  70. self.linkType = CPDFLinkTypeLink;
  71. self.segmentedControl.selectedSegmentIndex = 0;
  72. }
  73. }
  74. [self updatePreferredContentSizeWithTraitCollection:self.traitCollection];
  75. }
  76. -(void)viewDidAppear:(BOOL)animated {
  77. [super viewDidAppear:animated];
  78. UITextField *currentTextField = nil;
  79. switch (_linkType) {
  80. case CPDFLinkTypeLink:
  81. currentTextField = self.urlTextField;
  82. break;
  83. case CPDFLinkTypePage:
  84. currentTextField = self.pageTextField;
  85. break;
  86. default:
  87. case CPDFLinkTypeEmail:
  88. currentTextField = self.emailTextField;
  89. break;
  90. }
  91. [currentTextField becomeFirstResponder];
  92. }
  93. - (void)viewWillDisappear:(BOOL)animated {
  94. }
  95. - (BOOL)isLink {
  96. NSString *currentTextField = nil;
  97. switch (self.segmentedControl.selectedSegmentIndex) {
  98. case 0:
  99. currentTextField = self.urlTextField.text;
  100. break;
  101. case 1:
  102. currentTextField = self.pageTextField.text;
  103. break;
  104. default:
  105. case 2:
  106. currentTextField = self.emailTextField.text;
  107. break;
  108. }
  109. if (!([currentTextField isEqual:@""])) {
  110. return YES;
  111. } else {
  112. return NO;
  113. }
  114. }
  115. - (void)setLinkType:(CPDFLinkType)linkType {
  116. _linkType = linkType;
  117. UITextField *currentTextField = nil;
  118. switch (_linkType) {
  119. case CPDFLinkTypeLink:
  120. self.pageTextField.hidden = self.emailTextField.hidden = YES;
  121. self.urlTextField.hidden = NO;
  122. currentTextField = self.urlTextField;
  123. break;
  124. case CPDFLinkTypePage:
  125. self.urlTextField.hidden = self.emailTextField.hidden = YES;
  126. self.pageTextField.hidden = NO;
  127. currentTextField = self.pageTextField;
  128. break;
  129. default:
  130. case CPDFLinkTypeEmail:
  131. self.urlTextField.hidden = self.pageTextField.hidden = YES;
  132. self.emailTextField.hidden = NO;
  133. currentTextField = self.emailTextField;
  134. break;
  135. }
  136. [currentTextField becomeFirstResponder];
  137. if(currentTextField.text.length > 0) {
  138. self.saveButton.enabled = YES;
  139. self.saveButton.backgroundColor = [UIColor systemBlueColor];
  140. [self.saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  141. } else {
  142. self.saveButton.enabled = NO;
  143. self.saveButton.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
  144. [self.saveButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
  145. }
  146. }
  147. - (void)viewWillLayoutSubviews {
  148. [super viewWillLayoutSubviews];
  149. self.titleLabel.frame = CGRectMake((self.view.frame.size.width - 120)/2, 5, 120, 50);
  150. self.headerView.frame = CGRectMake(0, 0, self.view.frame.size.width, 50);
  151. if (@available(iOS 11.0, *)) {
  152. self.backBtn.frame = CGRectMake(self.view.frame.size.width - 60 - self.view.safeAreaInsets.right, 5, 50, 50);
  153. } else {
  154. self.backBtn.frame = CGRectMake(self.view.frame.size.width - 60, 5, 50, 50);
  155. }
  156. if (@available(iOS 11.0, *)) {
  157. _scrcollView.frame = CGRectMake(self.view.safeAreaInsets.left, 50, self.view.frame.size.width - self.view.safeAreaInsets.left - self.view.safeAreaInsets.right, self.view.frame.size.height);
  158. } else {
  159. _scrcollView.frame = CGRectMake(0, 50, self.view.frame.size.width, self.view.frame.size.height);
  160. }
  161. self.scrcollView.contentSize = CGSizeMake(_scrcollView.frame.size.width, self.scrcollView.contentSize.height);
  162. self.saveButton.frame = CGRectMake((self.scrcollView.frame.size.width - 120)/2, self.saveButton.frame.origin.y, 120, 32);
  163. }
  164. - (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  165. [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
  166. [self updatePreferredContentSizeWithTraitCollection:newCollection];
  167. }
  168. - (void)updatePreferredContentSizeWithTraitCollection:(UITraitCollection *)traitCollection {
  169. self.preferredContentSize = CGSizeMake(self.view.bounds.size.width, traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact ? 350 : 600);
  170. [self.urlTextField resignFirstResponder];
  171. [self.emailTextField resignFirstResponder];
  172. [self.pageTextField resignFirstResponder];
  173. }
  174. #pragma mark - Private
  175. - (void)initWithView {
  176. _titleLabel = [[UILabel alloc] init];
  177. _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
  178. _titleLabel.text = NSLocalizedString(@"Link to", nil);
  179. _titleLabel.textAlignment = NSTextAlignmentCenter;
  180. _titleLabel.font = [UIFont systemFontOfSize:20];
  181. _titleLabel.adjustsFontSizeToFitWidth = YES;
  182. [self.headerView addSubview:_titleLabel];
  183. _scrcollView = [[UIScrollView alloc] init];
  184. _scrcollView.frame = CGRectMake(0, 50, self.view.frame.size.width, self.view.frame.size.height);
  185. _scrcollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  186. _scrcollView.scrollEnabled = YES;
  187. [self.view addSubview:_scrcollView];
  188. self.backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  189. if (@available(iOS 11.0, *)) {
  190. self.backBtn.frame = CGRectMake(self.view.frame.size.width - 60 - self.view.safeAreaInsets.right, 5, 50, 50);
  191. } else {
  192. self.backBtn.frame = CGRectMake(self.view.frame.size.width - 60, 5, 50, 50);
  193. }
  194. self.backBtn.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  195. [self.backBtn setImage:[UIImage imageNamed:@"CPDFAnnotationBaseImageBack" inBundle:[NSBundle bundleForClass:self.class] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
  196. [self.backBtn addTarget:self action:@selector(buttonItemClicked_back:) forControlEvents:UIControlEventTouchUpInside];
  197. [self.headerView addSubview:self.backBtn];
  198. CGFloat offstY = 10;
  199. _segmentedControl = [[UISegmentedControl alloc] initWithItems:@[NSLocalizedString(@"URL", nil), NSLocalizedString(@"Page",nil),NSLocalizedString(@"Email",nil)]];
  200. _segmentedControl.frame = CGRectMake(30, offstY, self.scrcollView.frame.size.width - 30 *2, 32.0);
  201. _segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  202. [_segmentedControl addTarget:self action:@selector(segmentedControlValueChanged_Mode:) forControlEvents:UIControlEventValueChanged];
  203. [self.scrcollView addSubview:_segmentedControl];
  204. offstY +=_segmentedControl.frame.size.height;
  205. offstY+= 32.0;
  206. _urlTextField = [[UITextField alloc]initWithFrame:CGRectMake(30.0, offstY, self.scrcollView.frame.size.width - 60.0, 28.0)];
  207. _urlTextField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  208. _urlTextField.layer.borderWidth = 1.0;
  209. _urlTextField.layer.borderColor = [UIColor lightGrayColor].CGColor;
  210. _urlTextField.layer.cornerRadius = 5.0;
  211. _urlTextField.leftView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  212. _urlTextField.leftViewMode = UITextFieldViewModeAlways;
  213. _urlTextField.rightView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  214. _urlTextField.rightViewMode = UITextFieldViewModeAlways;
  215. _urlTextField.delegate = self;
  216. _urlTextField.hidden = YES;
  217. _urlTextField.font = [UIFont systemFontOfSize:18.0];
  218. _urlTextField.placeholder = @"https://www.compdf.com";
  219. [self.scrcollView addSubview:_urlTextField];
  220. _pageTextField = [[UITextField alloc]initWithFrame:CGRectMake(30.0, offstY, self.view.frame.size.width - 60.0, 28.0)];
  221. _pageTextField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  222. _pageTextField.leftView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  223. _pageTextField.layer.borderColor = [UIColor lightGrayColor].CGColor;
  224. _pageTextField.leftViewMode = UITextFieldViewModeAlways;
  225. _pageTextField.rightView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  226. _pageTextField.rightViewMode = UITextFieldViewModeAlways;
  227. _pageTextField.hidden = YES;
  228. _pageTextField.layer.borderWidth = 1.0;
  229. _pageTextField.layer.cornerRadius = 5.0;
  230. _pageTextField.delegate = self;
  231. _pageTextField.font = [UIFont systemFontOfSize:18.0];
  232. NSString *str = [NSString stringWithFormat:@"1~%ld", self.pageCount];
  233. _pageTextField.placeholder = NSLocalizedString(str, nil);
  234. [_pageTextField setKeyboardType:UIKeyboardTypeNumberPad];
  235. [self.scrcollView addSubview:_pageTextField];
  236. _emailTextField = [[UITextField alloc]initWithFrame:CGRectMake(30.0, offstY, self.view.frame.size.width - 60.0, 28.0)];
  237. _emailTextField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  238. _emailTextField.leftView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  239. _emailTextField.layer.borderColor = [UIColor lightGrayColor].CGColor;
  240. _emailTextField.leftViewMode = UITextFieldViewModeAlways;
  241. _emailTextField.rightView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];
  242. _emailTextField.rightViewMode = UITextFieldViewModeAlways;
  243. _emailTextField.hidden = YES;
  244. _emailTextField.layer.borderWidth = 1.0;
  245. _emailTextField.layer.cornerRadius = 5.0;
  246. _emailTextField.delegate = self;
  247. _emailTextField.font = [UIFont systemFontOfSize:18.0];
  248. _emailTextField.placeholder = NSLocalizedString(@"support@compdf.com", nil);
  249. [self.scrcollView addSubview:_emailTextField];
  250. offstY +=_urlTextField.frame.size.height;
  251. [_emailTextField addTarget:self action:@selector(textFieldTextChange:) forControlEvents:UIControlEventEditingChanged];
  252. [_pageTextField addTarget:self action:@selector(textFieldTextChange:) forControlEvents:UIControlEventEditingChanged];
  253. [_urlTextField addTarget:self action:@selector(textFieldTextChange:) forControlEvents:UIControlEventEditingChanged];
  254. offstY+= 30.0;
  255. self.saveButton = [UIButton buttonWithType:UIButtonTypeCustom];
  256. self.saveButton.frame = CGRectMake((self.scrcollView.frame.size.width - 120)/2, offstY, 120, 32);
  257. self.saveButton.layer.cornerRadius = 5.0;
  258. self.saveButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  259. [self.saveButton setTitle:NSLocalizedString(@"Save",nil) forState:UIControlStateNormal];
  260. [self.saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  261. [self.saveButton addTarget:self action:@selector(buttonItemClicked_Save:) forControlEvents:UIControlEventTouchUpInside];
  262. self.saveButton.backgroundColor = [UIColor systemBlueColor];
  263. [self.scrcollView addSubview:self.saveButton];
  264. offstY += self.saveButton.frame.size.height;
  265. self.scrcollView.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
  266. [[NSNotificationCenter defaultCenter] addObserver:self
  267. selector:@selector(keyboardwillChangeFrame:)
  268. name:UIKeyboardWillChangeFrameNotification
  269. object:nil];
  270. [[NSNotificationCenter defaultCenter] addObserver:self
  271. selector:@selector(keyboardWillHide:)
  272. name:UIKeyboardWillHideNotification
  273. object:nil];
  274. }
  275. #pragma mark - Action
  276. - (void)buttonItemClicked_back:(id)sender {
  277. NSString *currentTextField = nil;
  278. switch (self.segmentedControl.selectedSegmentIndex) {
  279. case 0:
  280. currentTextField = self.urlTextField.text;
  281. break;
  282. case 1:
  283. currentTextField = self.pageTextField.text;
  284. break;
  285. default:
  286. case 2:
  287. currentTextField = self.emailTextField.text;
  288. break;
  289. }
  290. BOOL isLink;
  291. if (!([currentTextField isEqual:@""])) {
  292. isLink = YES;
  293. } else {
  294. isLink = NO;
  295. }
  296. [self dismissViewControllerAnimated:YES completion:^{
  297. if (self.delegate && [self.delegate respondsToSelector:@selector(linkViewControllerDismiss:isLink:)]) {
  298. [self.delegate linkViewControllerDismiss:self isLink:isLink];
  299. }
  300. }];
  301. }
  302. - (void)segmentedControlValueChanged_Mode:(id)sender {
  303. UITextField *currentTextField = nil;
  304. switch (self.segmentedControl.selectedSegmentIndex) {
  305. case 0:
  306. self.linkType = CPDFLinkTypeLink;
  307. currentTextField = self.urlTextField;
  308. break;
  309. case 1:
  310. self.linkType = CPDFLinkTypePage;
  311. currentTextField = self.pageTextField;
  312. break;
  313. default:
  314. case 2:
  315. self.linkType = CPDFLinkTypeEmail;
  316. currentTextField = self.emailTextField;
  317. break;
  318. }
  319. }
  320. - (void)buttonItemClicked_Save:(id)sender {
  321. NSString *string = nil;
  322. if(CPDFLinkTypeLink == self.linkType) {
  323. string = self.urlTextField.text;
  324. string = [string lowercaseString];
  325. if (![string hasPrefix:@"https://"] && ![string hasPrefix:@"http://"]) {
  326. string = [NSString stringWithFormat:@"https://%@",string];
  327. }
  328. } else if (CPDFLinkTypePage == self.linkType) {
  329. string = self.pageTextField.text;
  330. CPDFAnnotation *annotation = self.annotStyle.annotations.firstObject;
  331. CPDFDocument *document = annotation.page.document;
  332. if([string integerValue] > document.pageCount || [string intValue] < 1) {
  333. UIAlertController *alert = [UIAlertController alertControllerWithTitle:@""
  334. message:NSLocalizedString(@"Config Error", nil)
  335. preferredStyle:UIAlertControllerStyleAlert];
  336. UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  337. self.pageTextField.text = @"";
  338. }];
  339. [alert addAction:okAction];
  340. [self presentViewController:alert animated:YES completion:nil];
  341. return;
  342. }
  343. } else if (CPDFLinkTypeEmail == self.linkType) {
  344. string = self.emailTextField.text;
  345. if (![string hasPrefix:@"mailto:"]) {
  346. string = [NSString stringWithFormat:@"mailto:%@",string];
  347. }
  348. }
  349. if([self.annotStyle.annotations.firstObject isKindOfClass:[CPDFButtonWidgetAnnotation class]]){
  350. CPDFButtonWidgetAnnotation *mAnnotation = (CPDFButtonWidgetAnnotation*)self.annotStyle.annotations.firstObject;
  351. if(self.linkType == CPDFLinkTypeEmail || self.linkType == CPDFLinkTypeLink){
  352. CPDFURLAction * urlAction = [[CPDFURLAction alloc] initWithURL:string];
  353. [mAnnotation setAction:urlAction];
  354. }else{
  355. NSLog(@"Desting Nation");
  356. }
  357. }
  358. [self dismissViewControllerAnimated:YES completion:^{
  359. if([self.delegate respondsToSelector:@selector(linkViewController:linkType:linkString:)]) {
  360. [self.delegate linkViewController:self linkType:self.linkType linkString:string];
  361. }
  362. }];
  363. }
  364. #pragma mark - UITextFieldDelegate
  365. - (void)textFieldTextChange:(UITextField *)textField {
  366. if(textField.text.length > 0) {
  367. self.saveButton.enabled = YES;
  368. self.saveButton.backgroundColor = [UIColor colorWithRed:20.0/255.0 green:96.0/255.0 blue:243.0/255.0 alpha:1.0];
  369. [self.saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  370. } else {
  371. self.saveButton.enabled = NO;
  372. self.saveButton.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
  373. [self.saveButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
  374. }
  375. if (textField == self.pageTextField) {
  376. if ([self.pageTextField.text floatValue] > self.pageCount) {
  377. self.saveButton.enabled = NO;
  378. self.saveButton.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
  379. [self.saveButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
  380. }
  381. }
  382. }
  383. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  384. [textField resignFirstResponder];
  385. return YES;
  386. }
  387. #pragma mark - NSNotification
  388. - (void)keyboardwillChangeFrame:(NSNotification *)notification {
  389. NSDictionary *userInfo = [notification userInfo];
  390. NSValue *value = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
  391. CGRect frame = value.CGRectValue;
  392. CGRect rect = [self.urlTextField convertRect:self.urlTextField.frame toView:self.view];
  393. if(CGRectGetMaxY(rect) > self.view.frame.size.height - frame.size.height) {
  394. UIEdgeInsets insets = self.scrcollView.contentInset;
  395. insets.bottom = frame.size.height + self.urlTextField.frame.size.height;
  396. self.scrcollView.contentInset = insets;
  397. }
  398. }
  399. - (void)keyboardWillHide:(NSNotification *)notification {
  400. UIEdgeInsets insets = self.scrcollView.contentInset;
  401. insets.bottom = 0;
  402. self.scrcollView.contentInset = insets;
  403. }
  404. @end