ZSSRichTextEditor.m 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  1. //
  2. // ZSSRichTextEditorViewController.m
  3. // ZSSRichTextEditor
  4. //
  5. // Created by Nicholas Hubbard on 11/30/13.
  6. // Copyright (c) 2013 Zed Said Studio. All rights reserved.
  7. //
  8. #import <objc/runtime.h>
  9. #import <UIKit/UIKit.h>
  10. #import "ZSSRichTextEditor.h"
  11. #import "ZSSBarButtonItem.h"
  12. #import "HRColorUtil.h"
  13. #import "ZSSTextView.h"
  14. @interface UIWebView (HackishAccessoryHiding)
  15. @property (nonatomic, assign) BOOL hidesInputAccessoryView;
  16. @end
  17. @implementation UIWebView (HackishAccessoryHiding)
  18. static const char * const hackishFixClassName = ""; //"UIWebBrowserViewMinusAccessoryView";
  19. static Class hackishFixClass = Nil;
  20. - (UIView *)hackishlyFoundBrowserView {
  21. // UIScrollView *scrollView = self.scrollView;
  22. UIView *browserView = nil;
  23. /*
  24. for (UIView *subview in scrollView.subviews) {
  25. if ([NSStringFromClass([subview class]) hasPrefix:@"TWSWebBrowserView"]) {
  26. browserView = subview;
  27. break;
  28. }
  29. }
  30. */
  31. return browserView;
  32. }
  33. - (id)methodReturningNil {
  34. return nil;
  35. }
  36. - (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass {
  37. if (!hackishFixClass) {
  38. Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
  39. newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
  40. IMP nilImp = [self methodForSelector:@selector(methodReturningNil)];
  41. class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:");
  42. objc_registerClassPair(newClass);
  43. hackishFixClass = newClass;
  44. }
  45. }
  46. - (BOOL) hidesInputAccessoryView {
  47. UIView *browserView = [self hackishlyFoundBrowserView];
  48. return [browserView class] == hackishFixClass;
  49. }
  50. - (void) setHidesInputAccessoryView:(BOOL)value {
  51. UIView *browserView = [self hackishlyFoundBrowserView];
  52. if (browserView == nil) {
  53. return;
  54. }
  55. [self ensureHackishSubclassExistsOfBrowserViewClass:[browserView class]];
  56. if (value) {
  57. object_setClass(browserView, hackishFixClass);
  58. }
  59. else {
  60. Class normalClass = objc_getClass("UIWebBrowserView");
  61. object_setClass(browserView, normalClass);
  62. }
  63. [browserView reloadInputViews];
  64. }
  65. @end
  66. @interface ZSSRichTextEditor ()
  67. @property (nonatomic, strong) UIScrollView *toolBarScroll;
  68. @property (nonatomic, strong) UIToolbar *toolbar;
  69. @property (nonatomic, strong) UIView *toolbarHolder;
  70. @property (nonatomic, strong) NSString *htmlString;
  71. @property (nonatomic, strong) UIWebView *editorView;
  72. @property (nonatomic, strong) ZSSTextView *sourceView;
  73. @property (nonatomic) CGRect editorViewFrame;
  74. @property (nonatomic) BOOL resourcesLoaded;
  75. @property (nonatomic, strong) NSArray *editorItemsEnabled;
  76. @property (nonatomic, strong) UIAlertView *alertView;
  77. @property (nonatomic, strong) NSString *selectedLinkURL;
  78. @property (nonatomic, strong) NSString *selectedLinkTitle;
  79. @property (nonatomic, strong) NSString *selectedImageURL;
  80. @property (nonatomic, strong) NSString *selectedImageAlt;
  81. @property (nonatomic, strong) UIBarButtonItem *keyboardItem;
  82. @property (nonatomic, strong) NSMutableArray *customBarButtonItems;
  83. @property (nonatomic, strong) NSMutableArray *customZSSBarButtonItems;
  84. @property (nonatomic, strong) NSString *internalHTML;
  85. @property (nonatomic) BOOL editorLoaded;
  86. - (NSString *)removeQuotesFromHTML:(NSString *)html;
  87. - (NSString *)tidyHTML:(NSString *)html;
  88. - (void)enableToolbarItems:(BOOL)enable;
  89. - (BOOL)isIpad;
  90. @end
  91. @implementation ZSSRichTextEditor
  92. - (void)viewDidLoad {
  93. [super viewDidLoad];
  94. self.editorLoaded = NO;
  95. self.shouldShowKeyboard = YES;
  96. self.formatHTML = YES;
  97. self.enabledToolbarItems = [[NSArray alloc] init];
  98. // Source View
  99. CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
  100. self.sourceView = [[ZSSTextView alloc] initWithFrame:frame];
  101. self.sourceView.hidden = YES;
  102. self.sourceView.autocapitalizationType = UITextAutocapitalizationTypeNone;
  103. self.sourceView.autocorrectionType = UITextAutocorrectionTypeNo;
  104. self.sourceView.font = [UIFont fontWithName:@"Courier" size:13.0];
  105. self.sourceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  106. self.sourceView.autoresizesSubviews = YES;
  107. self.sourceView.delegate = self;
  108. [self.view addSubview:self.sourceView];
  109. // Editor View
  110. self.editorView = [[UIWebView alloc] initWithFrame:frame];
  111. self.editorView.delegate = self;
  112. self.editorView.hidesInputAccessoryView = YES;
  113. self.editorView.keyboardDisplayRequiresUserAction = NO;
  114. self.editorView.scalesPageToFit = YES;
  115. self.editorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
  116. self.editorView.dataDetectorTypes = UIDataDetectorTypeNone;
  117. self.editorView.scrollView.bounces = NO;
  118. self.editorView.backgroundColor = [UIColor whiteColor];
  119. [self.view addSubview:self.editorView];
  120. // Scrolling View
  121. self.toolBarScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, [self isIpad] ? self.view.frame.size.width : self.view.frame.size.width - 44, 44)];
  122. self.toolBarScroll.backgroundColor = [UIColor clearColor];
  123. self.toolBarScroll.showsHorizontalScrollIndicator = NO;
  124. // Toolbar with icons
  125. self.toolbar = [[UIToolbar alloc] initWithFrame:self.toolBarScroll.frame];
  126. self.toolbar.backgroundColor = [UIColor clearColor];
  127. [self.toolBarScroll addSubview:self.toolbar];
  128. self.toolBarScroll.autoresizingMask = self.toolbar.autoresizingMask;
  129. // Background Toolbar
  130. UIToolbar *backgroundToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
  131. backgroundToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  132. // Parent holding view
  133. self.toolbarHolder = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 44)];
  134. self.toolbarHolder.autoresizingMask = self.toolbar.autoresizingMask;
  135. [self.toolbarHolder addSubview:self.toolBarScroll];
  136. [self.toolbarHolder insertSubview:backgroundToolbar atIndex:0];
  137. // Hide Keyboard
  138. if (![self isIpad]) {
  139. // Toolbar holder used to crop and position toolbar
  140. UIView *toolbarCropper = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width-44, 0, 44, 44)];
  141. toolbarCropper.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  142. toolbarCropper.clipsToBounds = YES;
  143. // Use a toolbar so that we can tint
  144. UIToolbar *keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(-7, -1, 44, 44)];
  145. [toolbarCropper addSubview:keyboardToolbar];
  146. self.keyboardItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSkeyboard.png"] style:UIBarButtonItemStylePlain target:self action:@selector(dismissKeyboard)];
  147. keyboardToolbar.items = @[self.keyboardItem];
  148. [self.toolbarHolder addSubview:toolbarCropper];
  149. UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0.6f, 44)];
  150. line.backgroundColor = [UIColor lightGrayColor];
  151. line.alpha = 0.7f;
  152. [toolbarCropper addSubview:line];
  153. }
  154. [self.view addSubview:self.toolbarHolder];
  155. // Build the toolbar
  156. [self buildToolbar];
  157. if (!self.resourcesLoaded) {
  158. NSString *filePath = [[NSBundle mainBundle] pathForResource:@"editor" ofType:@"html"];
  159. NSData *htmlData = [NSData dataWithContentsOfFile:filePath];
  160. NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
  161. NSString *source = [[NSBundle mainBundle] pathForResource:@"ZSSRichTextEditor" ofType:@"js"];
  162. NSString *jsString = [[NSString alloc] initWithData:[NSData dataWithContentsOfFile:source] encoding:NSUTF8StringEncoding];
  163. htmlString = [htmlString stringByReplacingOccurrencesOfString:@"<!--editor-->" withString:jsString];
  164. [self.editorView loadHTMLString:htmlString baseURL:self.baseURL];
  165. self.resourcesLoaded = YES;
  166. }
  167. }
  168. - (void)setEnabledToolbarItems:(NSArray *)enabledToolbarItems {
  169. _enabledToolbarItems = enabledToolbarItems;
  170. [self buildToolbar];
  171. }
  172. - (void)setToolbarItemTintColor:(UIColor *)toolbarItemTintColor {
  173. _toolbarItemTintColor = toolbarItemTintColor;
  174. // Update the color
  175. for (ZSSBarButtonItem *item in self.toolbar.items) {
  176. item.tintColor = [self barButtonItemDefaultColor];
  177. }
  178. self.keyboardItem.tintColor = toolbarItemTintColor;
  179. }
  180. - (void)setToolbarItemSelectedTintColor:(UIColor *)toolbarItemSelectedTintColor {
  181. _toolbarItemSelectedTintColor = toolbarItemSelectedTintColor;
  182. }
  183. - (void)setPlaceholderText {
  184. NSString *js = [NSString stringWithFormat:@"zss_editor.setPlaceholder(\"%@\");", self.placeholder];
  185. [self.editorView stringByEvaluatingJavaScriptFromString:js];
  186. }
  187. - (void)setFooterHeight:(float)footerHeight {
  188. NSString *js = [NSString stringWithFormat:@"zss_editor.setFooterHeight(\"%f\");", footerHeight];
  189. [self.editorView stringByEvaluatingJavaScriptFromString:js];
  190. }
  191. - (void)setContentHeight:(float)contentHeight {
  192. NSString *js = [NSString stringWithFormat:@"zss_editor.contentHeight = %f;", contentHeight];
  193. [self.editorView stringByEvaluatingJavaScriptFromString:js];
  194. }
  195. - (NSArray *)itemsForToolbar {
  196. NSMutableArray *items = [[NSMutableArray alloc] init];
  197. // None
  198. if(_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarNone])
  199. {
  200. return items;
  201. }
  202. // Bold
  203. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarBold]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  204. ZSSBarButtonItem *bold = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSbold.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setBold)];
  205. bold.label = @"bold";
  206. [items addObject:bold];
  207. }
  208. // Italic
  209. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarItalic]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  210. ZSSBarButtonItem *italic = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSitalic.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setItalic)];
  211. italic.label = @"italic";
  212. [items addObject:italic];
  213. }
  214. // Subscript
  215. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarSubscript]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  216. ZSSBarButtonItem *subscript = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSsubscript.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setSubscript)];
  217. subscript.label = @"subscript";
  218. [items addObject:subscript];
  219. }
  220. // Superscript
  221. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarSuperscript]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  222. ZSSBarButtonItem *superscript = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSsuperscript.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setSuperscript)];
  223. superscript.label = @"superscript";
  224. [items addObject:superscript];
  225. }
  226. // Strike Through
  227. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarStrikeThrough]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  228. ZSSBarButtonItem *strikeThrough = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSstrikethrough.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setStrikethrough)];
  229. strikeThrough.label = @"strikeThrough";
  230. [items addObject:strikeThrough];
  231. }
  232. // Underline
  233. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarUnderline]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  234. ZSSBarButtonItem *underline = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSunderline.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setUnderline)];
  235. underline.label = @"underline";
  236. [items addObject:underline];
  237. }
  238. // Remove Format
  239. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarRemoveFormat]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  240. ZSSBarButtonItem *removeFormat = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSclearstyle.png"] style:UIBarButtonItemStylePlain target:self action:@selector(removeFormat)];
  241. removeFormat.label = @"removeFormat";
  242. [items addObject:removeFormat];
  243. }
  244. // Undo
  245. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarUndo]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  246. ZSSBarButtonItem *undoButton = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSundo.png"] style:UIBarButtonItemStylePlain target:self action:@selector(undo:)];
  247. undoButton.label = @"undo";
  248. [items addObject:undoButton];
  249. }
  250. // Redo
  251. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarRedo]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  252. ZSSBarButtonItem *redoButton = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSredo.png"] style:UIBarButtonItemStylePlain target:self action:@selector(redo:)];
  253. redoButton.label = @"redo";
  254. [items addObject:redoButton];
  255. }
  256. // Align Left
  257. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarJustifyLeft]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  258. ZSSBarButtonItem *alignLeft = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSleftjustify.png"] style:UIBarButtonItemStylePlain target:self action:@selector(alignLeft)];
  259. alignLeft.label = @"justifyLeft";
  260. [items addObject:alignLeft];
  261. }
  262. // Align Center
  263. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarJustifyCenter]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  264. ZSSBarButtonItem *alignCenter = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSScenterjustify.png"] style:UIBarButtonItemStylePlain target:self action:@selector(alignCenter)];
  265. alignCenter.label = @"justifyCenter";
  266. [items addObject:alignCenter];
  267. }
  268. // Align Right
  269. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarJustifyRight]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  270. ZSSBarButtonItem *alignRight = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSrightjustify.png"] style:UIBarButtonItemStylePlain target:self action:@selector(alignRight)];
  271. alignRight.label = @"justifyRight";
  272. [items addObject:alignRight];
  273. }
  274. // Align Justify
  275. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarJustifyFull]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  276. ZSSBarButtonItem *alignFull = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSforcejustify.png"] style:UIBarButtonItemStylePlain target:self action:@selector(alignFull)];
  277. alignFull.label = @"justifyFull";
  278. [items addObject:alignFull];
  279. }
  280. // Paragraph
  281. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarParagraph]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  282. ZSSBarButtonItem *paragraph = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSparagraph.png"] style:UIBarButtonItemStylePlain target:self action:@selector(paragraph)];
  283. paragraph.label = @"p";
  284. [items addObject:paragraph];
  285. }
  286. // Header 1
  287. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH1]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  288. ZSSBarButtonItem *h1 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh1.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading1)];
  289. h1.label = @"h1";
  290. [items addObject:h1];
  291. }
  292. // Header 2
  293. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH2]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  294. ZSSBarButtonItem *h2 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh2.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading2)];
  295. h2.label = @"h2";
  296. [items addObject:h2];
  297. }
  298. // Header 3
  299. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH3]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  300. ZSSBarButtonItem *h3 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh3.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading3)];
  301. h3.label = @"h3";
  302. [items addObject:h3];
  303. }
  304. // Heading 4
  305. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH4]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  306. ZSSBarButtonItem *h4 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh4.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading4)];
  307. h4.label = @"h4";
  308. [items addObject:h4];
  309. }
  310. // Header 5
  311. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH5]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  312. ZSSBarButtonItem *h5 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh5.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading5)];
  313. h5.label = @"h5";
  314. [items addObject:h5];
  315. }
  316. // Heading 6
  317. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarH6]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  318. ZSSBarButtonItem *h6 = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSh6.png"] style:UIBarButtonItemStylePlain target:self action:@selector(heading6)];
  319. h6.label = @"h6";
  320. [items addObject:h6];
  321. }
  322. // Text Color
  323. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarTextColor]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  324. ZSSBarButtonItem *textColor = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSStextcolor.png"] style:UIBarButtonItemStylePlain target:self action:@selector(textColor)];
  325. textColor.label = @"textColor";
  326. [items addObject:textColor];
  327. }
  328. // Background Color
  329. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarBackgroundColor]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  330. ZSSBarButtonItem *bgColor = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSbgcolor.png"] style:UIBarButtonItemStylePlain target:self action:@selector(bgColor)];
  331. bgColor.label = @"backgroundColor";
  332. [items addObject:bgColor];
  333. }
  334. // Unordered List
  335. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarUnorderedList]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  336. ZSSBarButtonItem *ul = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSunorderedlist.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setUnorderedList)];
  337. ul.label = @"unorderedList";
  338. [items addObject:ul];
  339. }
  340. // Ordered List
  341. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarOrderedList]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  342. ZSSBarButtonItem *ol = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSorderedlist.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setOrderedList)];
  343. ol.label = @"orderedList";
  344. [items addObject:ol];
  345. }
  346. // Horizontal Rule
  347. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarHorizontalRule]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  348. ZSSBarButtonItem *hr = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSShorizontalrule.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setHR)];
  349. hr.label = @"horizontalRule";
  350. [items addObject:hr];
  351. }
  352. // Indent
  353. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarIndent]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  354. ZSSBarButtonItem *indent = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSindent.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setIndent)];
  355. indent.label = @"indent";
  356. [items addObject:indent];
  357. }
  358. // Outdent
  359. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarOutdent]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  360. ZSSBarButtonItem *outdent = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSoutdent.png"] style:UIBarButtonItemStylePlain target:self action:@selector(setOutdent)];
  361. outdent.label = @"outdent";
  362. [items addObject:outdent];
  363. }
  364. // Image
  365. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarInsertImage]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  366. ZSSBarButtonItem *insertImage = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSimage.png"] style:UIBarButtonItemStylePlain target:self action:@selector(insertImage)];
  367. insertImage.label = @"image";
  368. [items addObject:insertImage];
  369. }
  370. // Insert Link
  371. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarInsertLink]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  372. ZSSBarButtonItem *insertLink = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSlink.png"] style:UIBarButtonItemStylePlain target:self action:@selector(insertLink)];
  373. insertLink.label = @"link";
  374. [items addObject:insertLink];
  375. }
  376. // Remove Link
  377. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarRemoveLink]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  378. ZSSBarButtonItem *removeLink = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSunlink.png"] style:UIBarButtonItemStylePlain target:self action:@selector(removeLink)];
  379. removeLink.label = @"removeLink";
  380. [items addObject:removeLink];
  381. }
  382. // Quick Link
  383. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarQuickLink]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  384. ZSSBarButtonItem *quickLink = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSquicklink.png"] style:UIBarButtonItemStylePlain target:self action:@selector(quickLink)];
  385. quickLink.label = @"quickLink";
  386. [items addObject:quickLink];
  387. }
  388. // Show Source
  389. if ((_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarViewSource]) || (_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarAll])) {
  390. ZSSBarButtonItem *showSource = [[ZSSBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ZSSviewsource.png"] style:UIBarButtonItemStylePlain target:self action:@selector(showHTMLSource:)];
  391. showSource.label = @"source";
  392. [items addObject:showSource];
  393. }
  394. return [NSArray arrayWithArray:items];
  395. }
  396. - (void)buildToolbar {
  397. // Check to see if we have any toolbar items, if not, add them all
  398. NSArray *items = [self itemsForToolbar];
  399. if (items.count == 0 && !(_enabledToolbarItems && [_enabledToolbarItems containsObject:ZSSRichTextEditorToolbarNone])) {
  400. _enabledToolbarItems = @[ZSSRichTextEditorToolbarAll];
  401. items = [self itemsForToolbar];
  402. }
  403. if (self.customZSSBarButtonItems != nil) {
  404. items = [items arrayByAddingObjectsFromArray:self.customZSSBarButtonItems];
  405. }
  406. // get the width before we add custom buttons
  407. CGFloat toolbarWidth = items.count == 0 ? 0.0f : (CGFloat)(items.count * 39) - 10;
  408. if(self.customBarButtonItems != nil)
  409. {
  410. items = [items arrayByAddingObjectsFromArray:self.customBarButtonItems];
  411. for(ZSSBarButtonItem *buttonItem in self.customBarButtonItems)
  412. {
  413. toolbarWidth += buttonItem.customView.frame.size.width + 11.0f;
  414. }
  415. }
  416. self.toolbar.items = items;
  417. for (ZSSBarButtonItem *item in items) {
  418. item.tintColor = [self barButtonItemDefaultColor];
  419. }
  420. self.toolbar.frame = CGRectMake(0, 0, toolbarWidth, 44);
  421. self.toolBarScroll.contentSize = CGSizeMake(self.toolbar.frame.size.width, 44);
  422. }
  423. - (void)viewWillAppear:(BOOL)animated {
  424. [super viewWillAppear:animated];
  425. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowOrHide:) name:UIKeyboardWillShowNotification object:nil];
  426. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowOrHide:) name:UIKeyboardWillHideNotification object:nil];
  427. }
  428. - (void)viewWillDisappear:(BOOL)animated {
  429. [super viewWillDisappear:animated];
  430. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
  431. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
  432. }
  433. - (void)didReceiveMemoryWarning
  434. {
  435. [super didReceiveMemoryWarning];
  436. // Dispose of any resources that can be recreated.
  437. }
  438. #pragma mark - Editor Interaction
  439. - (void)focusTextEditor {
  440. self.editorView.keyboardDisplayRequiresUserAction = NO;
  441. NSString *js = [NSString stringWithFormat:@"zss_editor.focusEditor();"];
  442. [self.editorView stringByEvaluatingJavaScriptFromString:js];
  443. }
  444. - (void)blurTextEditor {
  445. NSString *js = [NSString stringWithFormat:@"zss_editor.blurEditor();"];
  446. [self.editorView stringByEvaluatingJavaScriptFromString:js];
  447. }
  448. - (void)setHTML:(NSString *)html {
  449. self.internalHTML = html;
  450. if (self.editorLoaded) {
  451. [self updateHTML];
  452. }
  453. }
  454. - (void)updateHTML {
  455. NSString *html = self.internalHTML;
  456. self.sourceView.text = html;
  457. NSString *cleanedHTML = [self removeQuotesFromHTML:self.sourceView.text];
  458. NSString *trigger = [NSString stringWithFormat:@"zss_editor.setHTML(\"%@\");", cleanedHTML];
  459. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  460. }
  461. - (NSString *)getHTML {
  462. NSString *html = [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.getHTML();"];
  463. html = [self removeQuotesFromHTML:html];
  464. html = [self tidyHTML:html];
  465. return html;
  466. }
  467. - (void)insertHTML:(NSString *)html {
  468. NSString *cleanedHTML = [self removeQuotesFromHTML:html];
  469. NSString *trigger = [NSString stringWithFormat:@"zss_editor.insertHTML(\"%@\");", cleanedHTML];
  470. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  471. }
  472. - (NSString *)getText {
  473. return [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.getText();"];
  474. }
  475. - (void)dismissKeyboard {
  476. [self.view endEditing:YES];
  477. }
  478. - (void)showHTMLSource:(ZSSBarButtonItem *)barButtonItem {
  479. if (self.sourceView.hidden) {
  480. self.sourceView.text = [self getHTML];
  481. self.sourceView.hidden = NO;
  482. barButtonItem.tintColor = [UIColor blackColor];
  483. self.editorView.hidden = YES;
  484. [self enableToolbarItems:NO];
  485. } else {
  486. [self setHTML:self.sourceView.text];
  487. barButtonItem.tintColor = [self barButtonItemDefaultColor];
  488. self.sourceView.hidden = YES;
  489. self.editorView.hidden = NO;
  490. [self enableToolbarItems:YES];
  491. }
  492. }
  493. - (void)removeFormat {
  494. NSString *trigger = @"zss_editor.removeFormating();";
  495. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  496. }
  497. - (void)alignLeft {
  498. NSString *trigger = @"zss_editor.setJustifyLeft();";
  499. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  500. }
  501. - (void)alignCenter {
  502. NSString *trigger = @"zss_editor.setJustifyCenter();";
  503. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  504. }
  505. - (void)alignRight {
  506. NSString *trigger = @"zss_editor.setJustifyRight();";
  507. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  508. }
  509. - (void)alignFull {
  510. NSString *trigger = @"zss_editor.setJustifyFull();";
  511. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  512. }
  513. - (void)setBold {
  514. NSString *trigger = @"zss_editor.setBold();";
  515. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  516. }
  517. - (void)setItalic {
  518. NSString *trigger = @"zss_editor.setItalic();";
  519. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  520. }
  521. - (void)setSubscript {
  522. NSString *trigger = @"zss_editor.setSubscript();";
  523. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  524. }
  525. - (void)setUnderline {
  526. NSString *trigger = @"zss_editor.setUnderline();";
  527. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  528. }
  529. - (void)setSuperscript {
  530. NSString *trigger = @"zss_editor.setSuperscript();";
  531. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  532. }
  533. - (void)setStrikethrough {
  534. NSString *trigger = @"zss_editor.setStrikeThrough();";
  535. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  536. }
  537. - (void)setUnorderedList {
  538. NSString *trigger = @"zss_editor.setUnorderedList();";
  539. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  540. }
  541. - (void)setOrderedList {
  542. NSString *trigger = @"zss_editor.setOrderedList();";
  543. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  544. }
  545. - (void)setHR {
  546. NSString *trigger = @"zss_editor.setHorizontalRule();";
  547. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  548. }
  549. - (void)setIndent {
  550. NSString *trigger = @"zss_editor.setIndent();";
  551. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  552. }
  553. - (void)setOutdent {
  554. NSString *trigger = @"zss_editor.setOutdent();";
  555. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  556. }
  557. - (void)heading1 {
  558. NSString *trigger = @"zss_editor.setHeading('h1');";
  559. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  560. }
  561. - (void)heading2 {
  562. NSString *trigger = @"zss_editor.setHeading('h2');";
  563. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  564. }
  565. - (void)heading3 {
  566. NSString *trigger = @"zss_editor.setHeading('h3');";
  567. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  568. }
  569. - (void)heading4 {
  570. NSString *trigger = @"zss_editor.setHeading('h4');";
  571. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  572. }
  573. - (void)heading5 {
  574. NSString *trigger = @"zss_editor.setHeading('h5');";
  575. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  576. }
  577. - (void)heading6 {
  578. NSString *trigger = @"zss_editor.setHeading('h6');";
  579. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  580. }
  581. - (void)paragraph {
  582. NSString *trigger = @"zss_editor.setParagraph();";
  583. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  584. }
  585. - (void)textColor {
  586. // Save the selection location
  587. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.prepareInsert();"];
  588. // Call the picker
  589. HRColorPickerViewController *colorPicker = [HRColorPickerViewController cancelableFullColorPickerViewControllerWithColor:[UIColor whiteColor]];
  590. colorPicker.delegate = self;
  591. colorPicker.tag = 1;
  592. colorPicker.title = NSLocalizedString(@"Text Color", nil);
  593. [self.navigationController pushViewController:colorPicker animated:YES];
  594. }
  595. - (void)bgColor {
  596. // Save the selection location
  597. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.prepareInsert();"];
  598. // Call the picker
  599. HRColorPickerViewController *colorPicker = [HRColorPickerViewController cancelableFullColorPickerViewControllerWithColor:[UIColor whiteColor]];
  600. colorPicker.delegate = self;
  601. colorPicker.tag = 2;
  602. colorPicker.title = NSLocalizedString(@"BG Color", nil);
  603. [self.navigationController pushViewController:colorPicker animated:YES];
  604. }
  605. - (void)setSelectedColor:(UIColor*)color tag:(int)tag {
  606. NSString *hex = [NSString stringWithFormat:@"#%06x",HexColorFromUIColor(color)];
  607. NSString *trigger;
  608. if (tag == 1) {
  609. trigger = [NSString stringWithFormat:@"zss_editor.setTextColor(\"%@\");", hex];
  610. } else if (tag == 2) {
  611. trigger = [NSString stringWithFormat:@"zss_editor.setBackgroundColor(\"%@\");", hex];
  612. }
  613. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  614. }
  615. - (void)undo:(ZSSBarButtonItem *)barButtonItem {
  616. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.undo();"];
  617. }
  618. - (void)redo:(ZSSBarButtonItem *)barButtonItem {
  619. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.redo();"];
  620. }
  621. - (void)insertLink {
  622. // Save the selection location
  623. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.prepareInsert();"];
  624. // Show the dialog for inserting or editing a link
  625. [self showInsertLinkDialogWithLink:self.selectedLinkURL title:self.selectedLinkTitle];
  626. }
  627. - (void)showInsertLinkDialogWithLink:(NSString *)url title:(NSString *)title {
  628. // Insert Button Title
  629. NSString *insertButtonTitle = !self.selectedLinkURL ? NSLocalizedString(@"Insert", nil) : NSLocalizedString(@"Update", nil);
  630. // Picker Button
  631. UIButton *am = [UIButton buttonWithType:UIButtonTypeCustom];
  632. am.frame = CGRectMake(0, 0, 25, 25);
  633. [am setImage:[UIImage imageNamed:@"ZSSpicker.png"] forState:UIControlStateNormal];
  634. [am addTarget:self action:@selector(showInsertURLAlternatePicker) forControlEvents:UIControlEventTouchUpInside];
  635. if ([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)]) {
  636. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Insert Link", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
  637. [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
  638. textField.placeholder = NSLocalizedString(@"URL (required)", nil);
  639. if (url) {
  640. textField.text = url;
  641. }
  642. textField.rightView = am;
  643. textField.rightViewMode = UITextFieldViewModeAlways;
  644. textField.clearButtonMode = UITextFieldViewModeAlways;
  645. }];
  646. [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
  647. textField.placeholder = NSLocalizedString(@"Title", nil);
  648. textField.clearButtonMode = UITextFieldViewModeAlways;
  649. textField.secureTextEntry = NO;
  650. if (title) {
  651. textField.text = title;
  652. }
  653. }];
  654. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  655. [self focusTextEditor];
  656. }]];
  657. [alertController addAction:[UIAlertAction actionWithTitle:insertButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  658. UITextField *linkURL = [alertController.textFields objectAtIndex:0];
  659. UITextField *title = [alertController.textFields objectAtIndex:1];
  660. if (!self.selectedLinkURL) {
  661. [self insertLink:linkURL.text title:title.text];
  662. NSLog(@"insert link");
  663. } else {
  664. [self updateLink:linkURL.text title:title.text];
  665. }
  666. [self focusTextEditor];
  667. }]];
  668. [self presentViewController:alertController animated:YES completion:NULL];
  669. } else {
  670. self.alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Insert Link", nil) message:nil delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", nil) otherButtonTitles:insertButtonTitle, nil];
  671. self.alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
  672. self.alertView.tag = 2;
  673. UITextField *linkURL = [self.alertView textFieldAtIndex:0];
  674. linkURL.placeholder = NSLocalizedString(@"URL (required)", nil);
  675. if (url) {
  676. linkURL.text = url;
  677. }
  678. linkURL.rightView = am;
  679. linkURL.rightViewMode = UITextFieldViewModeAlways;
  680. UITextField *alt = [self.alertView textFieldAtIndex:1];
  681. alt.secureTextEntry = NO;
  682. alt.placeholder = NSLocalizedString(@"Title", nil);
  683. if (title) {
  684. alt.text = title;
  685. }
  686. [self.alertView show];
  687. }
  688. }
  689. - (void)insertLink:(NSString *)url title:(NSString *)title {
  690. NSString *trigger = [NSString stringWithFormat:@"zss_editor.insertLink(\"%@\", \"%@\");", url, title];
  691. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  692. }
  693. - (void)updateLink:(NSString *)url title:(NSString *)title {
  694. NSString *trigger = [NSString stringWithFormat:@"zss_editor.updateLink(\"%@\", \"%@\");", url, title];
  695. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  696. }
  697. - (void)dismissAlertView {
  698. [self.alertView dismissWithClickedButtonIndex:self.alertView.cancelButtonIndex animated:YES];
  699. }
  700. - (void)addCustomToolbarItemWithButton:(UIButton *)button
  701. {
  702. if(self.customBarButtonItems == nil)
  703. {
  704. self.customBarButtonItems = [NSMutableArray array];
  705. }
  706. button.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:28.5f];
  707. [button setTitleColor:[self barButtonItemDefaultColor] forState:UIControlStateNormal];
  708. [button setTitleColor:[self barButtonItemSelectedDefaultColor] forState:UIControlStateHighlighted];
  709. ZSSBarButtonItem *barButtonItem = [[ZSSBarButtonItem alloc] initWithCustomView:button];
  710. [self.customBarButtonItems addObject:barButtonItem];
  711. [self buildToolbar];
  712. }
  713. - (void)addCustomToolbarItem:(ZSSBarButtonItem *)item {
  714. if(self.customZSSBarButtonItems == nil)
  715. {
  716. self.customZSSBarButtonItems = [NSMutableArray array];
  717. }
  718. [self.customZSSBarButtonItems addObject:item];
  719. [self buildToolbar];
  720. }
  721. - (void)removeLink {
  722. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.unlink();"];
  723. }//end
  724. - (void)quickLink {
  725. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.quickLink();"];
  726. }
  727. - (void)insertImage {
  728. // Save the selection location
  729. [self.editorView stringByEvaluatingJavaScriptFromString:@"zss_editor.prepareInsert();"];
  730. [self showInsertImageDialogWithLink:self.selectedImageURL alt:self.selectedImageAlt];
  731. }
  732. - (void)showInsertImageDialogWithLink:(NSString *)url alt:(NSString *)alt {
  733. // Insert Button Title
  734. NSString *insertButtonTitle = !self.selectedImageURL ? NSLocalizedString(@"Insert", nil) : NSLocalizedString(@"Update", nil);
  735. // Picker Button
  736. UIButton *am = [UIButton buttonWithType:UIButtonTypeCustom];
  737. am.frame = CGRectMake(0, 0, 25, 25);
  738. [am setImage:[UIImage imageNamed:@"ZSSpicker.png"] forState:UIControlStateNormal];
  739. [am addTarget:self action:@selector(showInsertImageAlternatePicker) forControlEvents:UIControlEventTouchUpInside];
  740. if ([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)]) {
  741. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Insert Image", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
  742. [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
  743. textField.placeholder = NSLocalizedString(@"URL (required)", nil);
  744. if (url) {
  745. textField.text = url;
  746. }
  747. textField.rightView = am;
  748. textField.rightViewMode = UITextFieldViewModeAlways;
  749. textField.clearButtonMode = UITextFieldViewModeAlways;
  750. }];
  751. [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
  752. textField.placeholder = NSLocalizedString(@"Alt", nil);
  753. textField.clearButtonMode = UITextFieldViewModeAlways;
  754. textField.secureTextEntry = NO;
  755. if (alt) {
  756. textField.text = alt;
  757. }
  758. }];
  759. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  760. [self focusTextEditor];
  761. }]];
  762. [alertController addAction:[UIAlertAction actionWithTitle:insertButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  763. UITextField *imageURL = [alertController.textFields objectAtIndex:0];
  764. UITextField *alt = [alertController.textFields objectAtIndex:1];
  765. if (!self.selectedImageURL) {
  766. [self insertImage:imageURL.text alt:alt.text];
  767. } else {
  768. [self updateImage:imageURL.text alt:alt.text];
  769. }
  770. [self focusTextEditor];
  771. }]];
  772. [self presentViewController:alertController animated:YES completion:NULL];
  773. } else {
  774. self.alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Insert Image", nil) message:nil delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", nil) otherButtonTitles:insertButtonTitle, nil];
  775. self.alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
  776. self.alertView.tag = 1;
  777. UITextField *imageURL = [self.alertView textFieldAtIndex:0];
  778. imageURL.placeholder = NSLocalizedString(@"URL (required)", nil);
  779. if (url) {
  780. imageURL.text = url;
  781. }
  782. imageURL.rightView = am;
  783. imageURL.rightViewMode = UITextFieldViewModeAlways;
  784. imageURL.clearButtonMode = UITextFieldViewModeAlways;
  785. UITextField *alt1 = [self.alertView textFieldAtIndex:1];
  786. alt1.secureTextEntry = NO;
  787. alt1.placeholder = NSLocalizedString(@"Alt", nil);
  788. alt1.clearButtonMode = UITextFieldViewModeAlways;
  789. if (alt) {
  790. alt1.text = alt;
  791. }
  792. [self.alertView show];
  793. }
  794. }
  795. - (void)insertImage:(NSString *)url alt:(NSString *)alt {
  796. NSString *trigger = [NSString stringWithFormat:@"zss_editor.insertImage(\"%@\", \"%@\");", url, alt];
  797. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  798. }
  799. - (void)updateImage:(NSString *)url alt:(NSString *)alt {
  800. NSString *trigger = [NSString stringWithFormat:@"zss_editor.updateImage(\"%@\", \"%@\");", url, alt];
  801. [self.editorView stringByEvaluatingJavaScriptFromString:trigger];
  802. }
  803. - (void)updateToolBarWithButtonName:(NSString *)name {
  804. // Items that are enabled
  805. NSArray *itemNames = [name componentsSeparatedByString:@","];
  806. // Special case for link
  807. NSMutableArray *itemsModified = [[NSMutableArray alloc] init];
  808. for (NSString *linkItem in itemNames) {
  809. NSString *updatedItem = linkItem;
  810. if ([linkItem hasPrefix:@"link:"]) {
  811. updatedItem = @"link";
  812. self.selectedLinkURL = [linkItem stringByReplacingOccurrencesOfString:@"link:" withString:@""];
  813. } else if ([linkItem hasPrefix:@"link-title:"]) {
  814. self.selectedLinkTitle = [self stringByDecodingURLFormat:[linkItem stringByReplacingOccurrencesOfString:@"link-title:" withString:@""]];
  815. } else if ([linkItem hasPrefix:@"image:"]) {
  816. updatedItem = @"image";
  817. self.selectedImageURL = [linkItem stringByReplacingOccurrencesOfString:@"image:" withString:@""];
  818. } else if ([linkItem hasPrefix:@"image-alt:"]) {
  819. self.selectedImageAlt = [self stringByDecodingURLFormat:[linkItem stringByReplacingOccurrencesOfString:@"image-alt:" withString:@""]];
  820. } else {
  821. self.selectedImageURL = nil;
  822. self.selectedImageAlt = nil;
  823. self.selectedLinkURL = nil;
  824. self.selectedLinkTitle = nil;
  825. }
  826. [itemsModified addObject:updatedItem];
  827. }
  828. itemNames = [NSArray arrayWithArray:itemsModified];
  829. self.editorItemsEnabled = itemNames;
  830. // Highlight items
  831. NSArray *items = self.toolbar.items;
  832. for (ZSSBarButtonItem *item in items) {
  833. if ([itemNames containsObject:item.label]) {
  834. item.tintColor = [self barButtonItemSelectedDefaultColor];
  835. } else {
  836. item.tintColor = [self barButtonItemDefaultColor];
  837. }
  838. }//end
  839. }
  840. #pragma mark - UITextView Delegate
  841. - (void)textViewDidChange:(UITextView *)textView {
  842. CGRect line = [textView caretRectForPosition:textView.selectedTextRange.start];
  843. CGFloat overflow = line.origin.y + line.size.height - ( textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top );
  844. if ( overflow > 0 ) {
  845. // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it)
  846. // Scroll caret to visible area
  847. CGPoint offset = textView.contentOffset;
  848. offset.y += overflow + 7; // leave 7 pixels margin
  849. // Cannot animate with setContentOffset:animated: or caret will not appear
  850. [UIView animateWithDuration:.2 animations:^{
  851. [textView setContentOffset:offset];
  852. }];
  853. }
  854. }
  855. #pragma mark - UIWebView Delegate
  856. - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
  857. NSString *urlString = [[request URL] absoluteString];
  858. NSLog(@"web request");
  859. NSLog(@"%@", urlString);
  860. if (navigationType == UIWebViewNavigationTypeLinkClicked) {
  861. return NO;
  862. } else if ([urlString rangeOfString:@"callback://0/"].location != NSNotFound) {
  863. // We recieved the callback
  864. NSString *className = [urlString stringByReplacingOccurrencesOfString:@"callback://0/" withString:@""];
  865. [self updateToolBarWithButtonName:className];
  866. } else if ([urlString rangeOfString:@"debug://"].location != NSNotFound) {
  867. // We recieved the callback
  868. NSString *debug = [urlString stringByReplacingOccurrencesOfString:@"debug://" withString:@""];
  869. debug = [debug stringByReplacingPercentEscapesUsingEncoding:NSStringEncodingConversionAllowLossy];
  870. NSLog(@"%@", debug);
  871. } else if ([urlString rangeOfString:@"scroll://"].location != NSNotFound) {
  872. NSInteger position = [[urlString stringByReplacingOccurrencesOfString:@"scroll://" withString:@""] integerValue];
  873. [self editorDidScrollWithPosition:position];
  874. }
  875. return YES;
  876. }//end
  877. - (void)webViewDidFinishLoad:(UIWebView *)webView {
  878. self.editorLoaded = YES;
  879. //[self setPlaceholderText];
  880. if (!self.internalHTML) {
  881. self.internalHTML = @"";
  882. }
  883. [self updateHTML];
  884. if (self.shouldShowKeyboard) {
  885. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  886. [self focusTextEditor];
  887. });
  888. }
  889. }
  890. #pragma mark - Callbacks
  891. // Blank implementation
  892. - (void)editorDidScrollWithPosition:(NSInteger)position {
  893. }
  894. #pragma mark - AlertView
  895. - (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView {
  896. if (alertView.tag == 1) {
  897. UITextField *textField = [alertView textFieldAtIndex:0];
  898. UITextField *textField2 = [alertView textFieldAtIndex:1];
  899. if ([textField.text length] == 0 || [textField2.text length] == 0) {
  900. return NO;
  901. }
  902. } else if (alertView.tag == 2) {
  903. UITextField *textField = [alertView textFieldAtIndex:0];
  904. if ([textField.text length] == 0) {
  905. return NO;
  906. }
  907. }
  908. return YES;
  909. }
  910. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  911. if (alertView.tag == 1) {
  912. if (buttonIndex == 1) {
  913. UITextField *imageURL = [alertView textFieldAtIndex:0];
  914. UITextField *alt = [alertView textFieldAtIndex:1];
  915. if (!self.selectedImageURL) {
  916. [self insertImage:imageURL.text alt:alt.text];
  917. } else {
  918. [self updateImage:imageURL.text alt:alt.text];
  919. }
  920. }
  921. } else if (alertView.tag == 2) {
  922. if (buttonIndex == 1) {
  923. UITextField *linkURL = [alertView textFieldAtIndex:0];
  924. UITextField *title = [alertView textFieldAtIndex:1];
  925. if (!self.selectedLinkURL) {
  926. [self insertLink:linkURL.text title:title.text];
  927. } else {
  928. [self updateLink:linkURL.text title:title.text];
  929. }
  930. }
  931. }
  932. }
  933. #pragma mark - Asset Picker
  934. - (void)showInsertURLAlternatePicker {
  935. // Blank method. User should implement this in their subclass
  936. }
  937. - (void)showInsertImageAlternatePicker {
  938. // Blank method. User should implement this in their subclass
  939. }
  940. #pragma mark - Keyboard status
  941. - (void)keyboardWillShowOrHide:(NSNotification *)notification {
  942. // Orientation
  943. UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  944. // User Info
  945. NSDictionary *info = notification.userInfo;
  946. CGFloat duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
  947. int curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
  948. CGRect keyboardEnd = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
  949. // Toolbar Sizes
  950. CGFloat sizeOfToolbar = self.toolbarHolder.frame.size.height;
  951. // Keyboard Size
  952. //Checks if IOS8, gets correct keyboard height
  953. CGFloat keyboardHeight = UIInterfaceOrientationIsLandscape(orientation) ? ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.000000) ? keyboardEnd.size.height : keyboardEnd.size.width : keyboardEnd.size.height;
  954. // Correct Curve
  955. UIViewAnimationOptions animationOptions = curve << 16;
  956. if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
  957. [UIView animateWithDuration:duration delay:0 options:animationOptions animations:^{
  958. // Toolbar
  959. CGRect frame = self.toolbarHolder.frame;
  960. frame.origin.y = self.view.frame.size.height - (keyboardHeight + sizeOfToolbar);
  961. self.toolbarHolder.frame = frame;
  962. // Editor View
  963. const int extraHeight = 10;
  964. CGRect editorFrame = self.editorView.frame;
  965. editorFrame.size.height = (self.view.frame.size.height - keyboardHeight) - sizeOfToolbar - extraHeight;
  966. self.editorView.frame = editorFrame;
  967. self.editorViewFrame = self.editorView.frame;
  968. self.editorView.scrollView.contentInset = UIEdgeInsetsZero;
  969. self.editorView.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
  970. // Source View
  971. CGRect sourceFrame = self.sourceView.frame;
  972. sourceFrame.size.height = (self.view.frame.size.height - keyboardHeight) - sizeOfToolbar - extraHeight;
  973. self.sourceView.frame = sourceFrame;
  974. // Provide editor with keyboard height and editor view height
  975. [self setFooterHeight:(keyboardHeight - 8)];
  976. [self setContentHeight: self.editorViewFrame.size.height];
  977. } completion:nil];
  978. } else {
  979. [UIView animateWithDuration:duration delay:0 options:animationOptions animations:^{
  980. CGRect frame = self.toolbarHolder.frame;
  981. frame.origin.y = self.view.frame.size.height + keyboardHeight;
  982. self.toolbarHolder.frame = frame;
  983. // Editor View
  984. CGRect editorFrame = self.editorView.frame;
  985. editorFrame.size.height = self.view.frame.size.height;
  986. self.editorView.frame = editorFrame;
  987. self.editorViewFrame = self.editorView.frame;
  988. self.editorView.scrollView.contentInset = UIEdgeInsetsZero;
  989. self.editorView.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
  990. // Source View
  991. CGRect sourceFrame = self.sourceView.frame;
  992. sourceFrame.size.height = self.view.frame.size.height;
  993. self.sourceView.frame = sourceFrame;
  994. } completion:nil];
  995. }//end
  996. }
  997. #pragma mark - Utilities
  998. - (NSString *)removeQuotesFromHTML:(NSString *)html {
  999. html = [html stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
  1000. html = [html stringByReplacingOccurrencesOfString:@"“" withString:@"&quot;"];
  1001. html = [html stringByReplacingOccurrencesOfString:@"”" withString:@"&quot;"];
  1002. html = [html stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
  1003. html = [html stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
  1004. return html;
  1005. }//end
  1006. - (NSString *)tidyHTML:(NSString *)html {
  1007. html = [html stringByReplacingOccurrencesOfString:@"<br>" withString:@"<br />"];
  1008. html = [html stringByReplacingOccurrencesOfString:@"<hr>" withString:@"<hr />"];
  1009. if (self.formatHTML) {
  1010. html = [self.editorView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"style_html(\"%@\");", html]];
  1011. }
  1012. return html;
  1013. }//end
  1014. - (UIColor *)barButtonItemDefaultColor {
  1015. if (self.toolbarItemTintColor) {
  1016. return self.toolbarItemTintColor;
  1017. }
  1018. return [UIColor colorWithRed:0.0f/255.0f green:122.0f/255.0f blue:255.0f/255.0f alpha:1.0f];
  1019. }
  1020. - (UIColor *)barButtonItemSelectedDefaultColor {
  1021. if (self.toolbarItemSelectedTintColor) {
  1022. return self.toolbarItemSelectedTintColor;
  1023. }
  1024. return [UIColor blackColor];
  1025. }
  1026. - (BOOL)isIpad {
  1027. return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
  1028. }//end
  1029. - (NSString *)stringByDecodingURLFormat:(NSString *)string {
  1030. NSString *result = [string stringByReplacingOccurrencesOfString:@"+" withString:@" "];
  1031. result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  1032. return result;
  1033. }
  1034. - (void)enableToolbarItems:(BOOL)enable {
  1035. NSArray *items = self.toolbar.items;
  1036. for (ZSSBarButtonItem *item in items) {
  1037. if (![item.label isEqualToString:@"source"]) {
  1038. item.enabled = enable;
  1039. }
  1040. }
  1041. }
  1042. @end