Extension.swift 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  1. //
  2. // StringUtil.swift
  3. // Runner
  4. //
  5. // Created by Yayan Dwi on 20/04/20.
  6. // Copyright © 2020 The Chromium Authors. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. import SDWebImage
  11. extension Date {
  12. public func currentTimeMillis() -> Int {
  13. return Int(self.timeIntervalSince1970 * 1000)
  14. }
  15. func format(dateFormat: String) -> String {
  16. let formatter = DateFormatter()
  17. formatter.dateFormat = dateFormat
  18. return formatter.string(from: self)
  19. }
  20. var millisecondsSince1970:Int64 {
  21. return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
  22. }
  23. public init(milliseconds:Int64) {
  24. self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
  25. }
  26. }
  27. extension String {
  28. func toNormalString() -> String {
  29. let _source = self.replacingOccurrences(of: "+", with: "%20")
  30. if var result = _source.removingPercentEncoding {
  31. result = result.replacingOccurrences(of: "<NL>", with: "\n")
  32. result = result.replacingOccurrences(of: "<CR>", with: "\r")
  33. return decrypt(source: result)
  34. }
  35. return self
  36. }
  37. func toStupidString() -> String {
  38. if var result = self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
  39. result = result.replacingOccurrences(of: "\n", with: "<NL>")
  40. result = result.replacingOccurrences(of: "\r", with: "<CR>")
  41. result = result.replacingOccurrences(of: "+", with: "%2B")
  42. return result
  43. }
  44. return self
  45. }
  46. private func decrypt(source : String) -> String {
  47. if let result = source.removingPercentEncoding {
  48. return result
  49. }
  50. return source
  51. }
  52. public func matches(_ regex: String) -> Bool {
  53. return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
  54. }
  55. }
  56. extension Int {
  57. func toHex() -> String {
  58. return String(format: "%02X", self)
  59. }
  60. }
  61. extension UIApplication {
  62. public static var appVersion: String? {
  63. return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
  64. }
  65. var rootViewController: UIViewController? {
  66. return UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
  67. }
  68. public var visibleViewController: UIViewController? {
  69. let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
  70. if var topController = keyWindow?.rootViewController {
  71. while let presentedViewController = topController.presentedViewController {
  72. topController = presentedViewController
  73. }
  74. return topController
  75. }
  76. return nil
  77. }
  78. }
  79. extension UIView {
  80. public func anchor(top: NSLayoutYAxisAnchor? = nil,
  81. left: NSLayoutXAxisAnchor? = nil,
  82. bottom: NSLayoutYAxisAnchor? = nil,
  83. right: NSLayoutXAxisAnchor? = nil,
  84. paddingTop: CGFloat = 0,
  85. paddingLeft: CGFloat = 0,
  86. paddingBottom: CGFloat = 0,
  87. paddingRight: CGFloat = 0,
  88. centerX: NSLayoutXAxisAnchor? = nil,
  89. centerY: NSLayoutYAxisAnchor? = nil,
  90. width: CGFloat = 0,
  91. height: CGFloat = 0,
  92. minHeight: CGFloat = 0,
  93. maxHeight: CGFloat = 0,
  94. minWidth: CGFloat = 0,
  95. maxWidth: CGFloat = 0,
  96. dynamicLeft: Bool = false,
  97. dynamicRight: Bool = false) {
  98. translatesAutoresizingMaskIntoConstraints = false
  99. if let top = top {
  100. topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
  101. }
  102. if let left = left {
  103. if dynamicLeft {
  104. leftAnchor.constraint(greaterThanOrEqualTo: left, constant: paddingLeft).isActive = true
  105. } else {
  106. leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
  107. }
  108. }
  109. if let right = right {
  110. if dynamicRight {
  111. leftAnchor.constraint(lessThanOrEqualTo: right, constant: -paddingRight).isActive = true
  112. } else {
  113. rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
  114. }
  115. }
  116. if let bottom = bottom {
  117. bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
  118. }
  119. if let centerX = centerX {
  120. centerXAnchor.constraint(equalTo: centerX).isActive = true
  121. }
  122. if let centerY = centerY {
  123. centerYAnchor.constraint(equalTo: centerY).isActive = true
  124. }
  125. if height != 0 || minHeight != 0 || maxHeight != 0 {
  126. if minHeight != 0 && maxHeight != 0 {
  127. heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight).isActive = true
  128. heightAnchor.constraint(lessThanOrEqualToConstant: maxHeight).isActive = true
  129. } else if minHeight != 0 && maxHeight == 0 {
  130. heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight).isActive = true
  131. } else if minHeight == 0 && maxHeight != 0 {
  132. heightAnchor.constraint(lessThanOrEqualToConstant: maxHeight).isActive = true
  133. } else {
  134. heightAnchor.constraint(equalToConstant: height).isActive = true
  135. }
  136. }
  137. if width != 0 || minWidth != 0 || maxWidth != 0 {
  138. if minWidth != 0 && maxWidth != 0 {
  139. heightAnchor.constraint(greaterThanOrEqualToConstant: minWidth).isActive = true
  140. heightAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
  141. } else if minWidth != 0 && maxWidth == 0 {
  142. heightAnchor.constraint(greaterThanOrEqualToConstant: minWidth).isActive = true
  143. } else if minWidth == 0 && maxWidth != 0 {
  144. heightAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
  145. } else {
  146. widthAnchor.constraint(equalToConstant: width).isActive = true
  147. }
  148. }
  149. }
  150. }
  151. extension UIViewController {
  152. var previousViewController: UIViewController? {
  153. guard let navigationController = navigationController else { return nil }
  154. let count = navigationController.viewControllers.count
  155. return count < 2 ? nil : navigationController.viewControllers[count - 2]
  156. }
  157. }
  158. extension UIImage {
  159. func resize(target: CGSize) -> UIImage {
  160. // Determine the scale factor that preserves aspect ratio
  161. let widthRatio = target.width / size.width
  162. let heightRatio = target.height / size.height
  163. let scaleFactor = min(widthRatio, heightRatio)
  164. // Compute the new image size that preserves aspect ratio
  165. let scaledImageSize = CGSize(
  166. width: size.width * scaleFactor,
  167. height: size.height * scaleFactor
  168. )
  169. // Draw and return the resized UIImage
  170. let renderer = UIGraphicsImageRenderer(
  171. size: scaledImageSize
  172. )
  173. let scaledImage = renderer.image { _ in
  174. self.draw(in: CGRect(
  175. origin: .zero,
  176. size: scaledImageSize
  177. ))
  178. }
  179. return scaledImage
  180. }
  181. }
  182. extension UIImage {
  183. var isPortrait: Bool { size.height > size.width }
  184. var isLandscape: Bool { size.width > size.height }
  185. var breadth: CGFloat { min(size.width, size.height) }
  186. var breadthSize: CGSize { .init(width: breadth, height: breadth) }
  187. var breadthRect: CGRect { .init(origin: .zero, size: breadthSize) }
  188. public var circleMasked: UIImage? {
  189. guard let cgImage = cgImage?
  190. .cropping(to: .init(origin: .init(x: isLandscape ? ((size.width-size.height)/2).rounded(.down) : 0,
  191. y: isPortrait ? ((size.height-size.width)/2).rounded(.down) : 0),
  192. size: breadthSize)) else { return nil }
  193. let format = imageRendererFormat
  194. format.opaque = false
  195. return UIGraphicsImageRenderer(size: breadthSize, format: format).image { _ in
  196. UIBezierPath(ovalIn: breadthRect).addClip()
  197. UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation)
  198. .draw(in: .init(origin: .zero, size: breadthSize))
  199. }
  200. }
  201. }
  202. extension NSObject {
  203. private static var urlStore = [String:String]()
  204. public func getImage(name url: String, placeholderImage: UIImage? = nil, isCircle: Bool = false, tableView: UITableView? = nil, indexPath: IndexPath? = nil, completion: @escaping (Bool, Bool, UIImage?)->()) {
  205. let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
  206. type(of: self).urlStore[tmpAddress] = url
  207. if url.isEmpty {
  208. completion(false, false, placeholderImage)
  209. return
  210. }
  211. do {
  212. let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  213. let file = documentDir.appendingPathComponent(url)
  214. if FileManager().fileExists(atPath: file.path) {
  215. let image = UIImage(contentsOfFile: file.path)?.sd_resizedImage(with: CGSize(width: 400, height: 400), scaleMode: .aspectFill)
  216. completion(true, false, isCircle ? image?.circleMasked : image)
  217. } else {
  218. completion(false, false, placeholderImage)
  219. Download().start(forKey: url) { (name, progress) in
  220. guard progress == 100 else {
  221. return
  222. }
  223. DispatchQueue.main.async {
  224. if tableView != nil {
  225. tableView!.beginUpdates()
  226. tableView!.reloadRows(at: [indexPath!], with: .none)
  227. tableView!.endUpdates()
  228. }
  229. if type(of: self).urlStore[tmpAddress] == name {
  230. let image = UIImage(contentsOfFile: file.path)?.sd_resizedImage(with: CGSize(width: 400, height: 400), scaleMode: .aspectFill)
  231. completion(true, true, isCircle ? image?.circleMasked : image)
  232. }
  233. }
  234. }
  235. }
  236. } catch {}
  237. }
  238. func loadImage(named: String, placeholderImage: UIImage?, completion: @escaping (UIImage?, Bool) -> ()) {
  239. guard !named.isEmpty else {
  240. completion(placeholderImage, true)
  241. return
  242. }
  243. SDWebImageManager.shared.loadImage(with: URL.palioImage(named: named), options: .highPriority, progress: .none) { image, data, error, type, finish, url in
  244. completion(image, finish)
  245. }
  246. }
  247. public func deleteAllRecordDatabase() {
  248. Database.shared.database?.inTransaction({ fmdb, rollback in
  249. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "BUDDY", _where: "")
  250. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ", _where: "")
  251. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE", _where: "")
  252. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ_MEMBER", _where: "")
  253. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "DISCUSSION_FORUM", _where: "")
  254. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "POST", _where: "")
  255. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_STATUS", _where: "")
  256. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "")
  257. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "OUTGOING", _where: "")
  258. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FOLLOW", _where: "")
  259. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_FAVORITE", _where: "")
  260. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "LINK_PREVIEW", _where: "")
  261. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PULL_DB", _where: "")
  262. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PREFS", _where: "")
  263. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", _where: "")
  264. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM", _where: "")
  265. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_ITEM", _where: "")
  266. })
  267. }
  268. }
  269. extension URL {
  270. static func palioImage(named: String) -> URL? {
  271. return URL(string: "http://202.158.33.26/filepalio/image/\(named)")
  272. }
  273. }
  274. extension UIColor {
  275. public static var mainColor: UIColor {
  276. renderColor(hex: "#000000")
  277. }
  278. public static var secondaryColor: UIColor {
  279. return renderColor(hex: "#FAFAFF")
  280. }
  281. public static var orangeColor: UIColor {
  282. return renderColor(hex: "#FFA03E")
  283. }
  284. public static var orangeBNI: UIColor {
  285. return renderColor(hex: "#EE6600")
  286. }
  287. public static var greenColor: UIColor {
  288. return renderColor(hex: "#C7EA46")
  289. }
  290. public static var grayColor: UIColor {
  291. return renderColor(hex: "#F5F5F5")
  292. }
  293. public static var docColor: UIColor {
  294. return renderColor(hex: "#798F9A")
  295. }
  296. public static var linkColor: UIColor {
  297. return renderColor(hex: "#68BBE3")
  298. }
  299. public static var blueBubbleColor: UIColor {
  300. return renderColor(hex: "#C5D1E1")
  301. }
  302. public class func renderColor(hex: String) -> UIColor {
  303. var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
  304. if (cString.hasPrefix("#")) {
  305. cString.remove(at: cString.startIndex)
  306. }
  307. if ((cString.count) != 6) {
  308. return UIColor.gray
  309. }
  310. var rgbValue:UInt64 = 0
  311. Scanner(string: cString).scanHexInt64(&rgbValue)
  312. return UIColor(
  313. red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
  314. green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
  315. blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
  316. alpha: CGFloat(1.0)
  317. )
  318. }
  319. }
  320. extension UIView {
  321. public func circle() {
  322. layer.cornerRadius = 0.5 * bounds.size.width
  323. clipsToBounds = true
  324. }
  325. public func maxCornerRadius() -> CGFloat {
  326. return (self.frame.width > self.frame.height) ? self.frame.height / 2 : self.frame.width / 2
  327. }
  328. }
  329. extension String {
  330. public func localized(uppercased: Bool = false) -> String {
  331. if let _ = UserDefaults.standard.string(forKey: "i18n_language") {} else {
  332. // we set a default, just in case
  333. UserDefaults.standard.set("en", forKey: "i18n_language")
  334. UserDefaults.standard.synchronize()
  335. }
  336. let lang = UserDefaults.standard.string(forKey: "i18n_language")
  337. let bundle = Bundle.resourceBundle(for: Nexilis.self).path(forResource: lang, ofType: "lproj")
  338. let bundlePath = Bundle(path: bundle!)
  339. print()
  340. let result = NSLocalizedString(
  341. self,
  342. tableName: "Localizable",
  343. bundle: bundlePath!,
  344. value: self,
  345. comment: self)
  346. if uppercased {
  347. return result.uppercased()
  348. }
  349. return result
  350. }
  351. }
  352. extension UIViewController {
  353. public func showToast(message : String, font: UIFont = UIFont.systemFont(ofSize: 12, weight: .medium), controller: UIViewController) {
  354. let toastContainer = UIView(frame: CGRect())
  355. toastContainer.backgroundColor = UIColor.mainColor.withAlphaComponent(0.6)
  356. toastContainer.alpha = 0.0
  357. toastContainer.layer.cornerRadius = 25;
  358. toastContainer.clipsToBounds = true
  359. let toastLabel = UILabel(frame: CGRect())
  360. toastLabel.textColor = UIColor.white
  361. toastLabel.textAlignment = .center;
  362. toastLabel.font = font
  363. toastLabel.text = message
  364. toastLabel.clipsToBounds = true
  365. toastLabel.numberOfLines = 0
  366. toastContainer.addSubview(toastLabel)
  367. controller.view.addSubview(toastContainer)
  368. controller.view.bringSubviewToFront(toastContainer)
  369. toastLabel.translatesAutoresizingMaskIntoConstraints = false
  370. toastContainer.translatesAutoresizingMaskIntoConstraints = false
  371. let a1 = NSLayoutConstraint(item: toastLabel, attribute: .leading, relatedBy: .equal, toItem: toastContainer, attribute: .leading, multiplier: 1, constant: 15)
  372. let a2 = NSLayoutConstraint(item: toastLabel, attribute: .trailing, relatedBy: .equal, toItem: toastContainer, attribute: .trailing, multiplier: 1, constant: -15)
  373. let a3 = NSLayoutConstraint(item: toastLabel, attribute: .bottom, relatedBy: .equal, toItem: toastContainer, attribute: .bottom, multiplier: 1, constant: -15)
  374. let a4 = NSLayoutConstraint(item: toastLabel, attribute: .top, relatedBy: .equal, toItem: toastContainer, attribute: .top, multiplier: 1, constant: 15)
  375. toastContainer.addConstraints([a1, a2, a3, a4])
  376. let c1 = NSLayoutConstraint(item: toastContainer, attribute: .leading, relatedBy: .equal, toItem: controller.view, attribute: .leading, multiplier: 1, constant: 65)
  377. let c2 = NSLayoutConstraint(item: toastContainer, attribute: .trailing, relatedBy: .equal, toItem: controller.view, attribute: .trailing, multiplier: 1, constant: -65)
  378. let c3 = NSLayoutConstraint(item: toastContainer, attribute: .bottom, relatedBy: .equal, toItem: controller.view, attribute: .bottom, multiplier: 1, constant: -75)
  379. controller.view.addConstraints([c1, c2, c3])
  380. UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn, animations: {
  381. toastContainer.alpha = 1.0
  382. }, completion: { _ in
  383. UIView.animate(withDuration: 0.5, delay: 1.5, options: .curveEaseOut, animations: {
  384. toastContainer.alpha = 0.0
  385. }, completion: {_ in
  386. toastContainer.removeFromSuperview()
  387. })
  388. })
  389. }
  390. public func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
  391. UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0);
  392. image.draw(in: CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height))
  393. let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
  394. UIGraphicsEndImageContext()
  395. return newImage
  396. }
  397. }
  398. extension UITextView {
  399. enum ShouldChangeCursor {
  400. case incrementCursor
  401. case preserveCursor
  402. }
  403. func preserveCursorPosition(withChanges mutatingFunction: (UITextPosition?) -> (ShouldChangeCursor)) {
  404. //save the cursor positon
  405. var cursorPosition: UITextPosition? = nil
  406. if let selectedRange = self.selectedTextRange {
  407. let offset = self.offset(from: self.beginningOfDocument, to: selectedRange.start)
  408. cursorPosition = self.position(from: self.beginningOfDocument, offset: offset)
  409. }
  410. //make mutaing changes that may reset the cursor position
  411. let shouldChangeCursor = mutatingFunction(cursorPosition)
  412. //restore the cursor
  413. if var cursorPosition = cursorPosition {
  414. if shouldChangeCursor == .incrementCursor {
  415. cursorPosition = self.position(from: cursorPosition, offset: 1) ?? cursorPosition
  416. }
  417. if let range = self.textRange(from: cursorPosition, to: cursorPosition) {
  418. self.selectedTextRange = range
  419. }
  420. }
  421. }
  422. }
  423. extension String {
  424. public func substring(from: Int?, to: Int?) -> String {
  425. if let start = from {
  426. guard start < self.count else {
  427. return ""
  428. }
  429. }
  430. if let end = to {
  431. guard end >= 0 else {
  432. return ""
  433. }
  434. }
  435. if let start = from, let end = to {
  436. guard end - start >= 0 else {
  437. return ""
  438. }
  439. }
  440. let startIndex: String.Index
  441. if let start = from, start >= 0 {
  442. startIndex = self.index(self.startIndex, offsetBy: start)
  443. } else {
  444. startIndex = self.startIndex
  445. }
  446. let endIndex: String.Index
  447. if let end = to, end >= 0, end < self.count {
  448. endIndex = self.index(self.startIndex, offsetBy: end + 1)
  449. } else {
  450. endIndex = self.endIndex
  451. }
  452. return String(self[startIndex ..< endIndex])
  453. }
  454. func countEmojiCharacter() -> Int {
  455. func isEmoji(s:NSString) -> Bool {
  456. let high:Int = Int(s.character(at: 0))
  457. if 0xD800 <= high && high <= 0xDBFF {
  458. let low:Int = Int(s.character(at: 1))
  459. let codepoint: Int = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
  460. return (0x1D000 <= codepoint && codepoint <= 0x1F9FF)
  461. }
  462. else {
  463. return (0x2100 <= high && high <= 0x27BF)
  464. }
  465. }
  466. let nsString = self as NSString
  467. var length = 0
  468. nsString.enumerateSubstrings(in: NSMakeRange(0, nsString.length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (subString, substringRange, enclosingRange, stop) -> Void in
  469. if isEmoji(s: subString! as NSString) {
  470. length+=1
  471. }
  472. }
  473. return length
  474. }
  475. public func richText(isEditing: Bool = false, first: Int = 0, last: Int = 0, isSearching: Bool = false, textSearch: String = "") -> NSAttributedString {
  476. let font = UIFont.systemFont(ofSize: 12)
  477. let textUTF8 = String(self.utf8)
  478. let finalText = NSMutableAttributedString(string: textUTF8, attributes: [NSAttributedString.Key.font: font])
  479. var boolStar = false
  480. var startStar = 0
  481. var endStar = 0
  482. var boolItalic = false
  483. var startItalic = 0
  484. var endItalic = 0
  485. var boolUnderLine = false
  486. var startUnderLine = 0
  487. var endUnderLine = 0
  488. var boolStrike = false
  489. var startStrike = 0
  490. var endStrike = 0
  491. var firstCount = 0
  492. var lastCount = textUTF8.count - 1
  493. if isEditing {
  494. firstCount = first
  495. lastCount = last
  496. }
  497. if textUTF8.count > 0 {
  498. for i in firstCount...lastCount {
  499. //BOLD
  500. if String(textUTF8.substring(from: i, to: i)) == "*" && !boolStar {
  501. boolStar = true
  502. startStar = i
  503. startStar = startStar - (textUTF8.count - finalText.string.count)
  504. } else if String(textUTF8.substring(from: i, to: i)) == "*" && boolStar {
  505. endStar = i
  506. endStar = endStar - (self.count - finalText.string.count)
  507. //!String(textUTF8.substring(from: startStar + 1, to: endStar)).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  508. if (startStar - 1 == -1 || checkCharBeforeAfter(char: String(finalText.string.substring(from: startStar - 1, to: startStar - 1)))) && (endStar + 1 == finalText.string.count || checkCharBeforeAfter(char: String(finalText.string.substring(from: endStar + 1, to: endStar + 1)))) && endStar - startStar != 1 && !String(finalText.string.substring(from: startStar + 1, to: endStar - 1)).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
  509. let countEmoji = finalText.string.countEmojiCharacter()
  510. finalText.addAttribute(.font, value: font.bold, range: NSRange(location: startStar, length: endStar - startStar + countEmoji + 1))
  511. if !isEditing{
  512. finalText.mutableString.replaceOccurrences(of: "*", with: "", options: .literal, range: NSRange(location: startStar, length: endStar - startStar + countEmoji + 1))
  513. } else {
  514. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(startStar + countEmoji...startStar + countEmoji))
  515. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(endStar + countEmoji...endStar + countEmoji))
  516. }
  517. boolStar = false
  518. startStar = 0
  519. } else {
  520. startStar = i
  521. startStar = startStar - (textUTF8.count - finalText.string.count)
  522. }
  523. }
  524. //ITALIC
  525. if String(textUTF8.substring(from: i, to: i)) == "_" && !boolItalic {
  526. boolItalic = true
  527. startItalic = i
  528. startItalic = startItalic - (textUTF8.count - finalText.string.count)
  529. } else if String(textUTF8.substring(from: i, to: i)) == "_" && boolItalic {
  530. endItalic = i
  531. endItalic = endItalic - (textUTF8.count - finalText.string.count)
  532. if (startItalic - 1 == -1 || checkCharBeforeAfter(char: String(finalText.string.substring(from: startItalic - 1, to: startItalic - 1)))) && (endItalic + 1 == finalText.string.count || checkCharBeforeAfter(char: String(finalText.string.substring(from: endItalic + 1, to: endItalic + 1)))) && endItalic - startItalic != 1 && !String(finalText.string.substring(from: startItalic + 1, to: endItalic - 1)).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
  533. let countEmoji = finalText.string.countEmojiCharacter()
  534. finalText.addAttribute(.font, value: font.italic, range: NSRange(location: startItalic, length: endItalic - startItalic + (countEmoji + 1)))
  535. if !isEditing{
  536. finalText.mutableString.replaceOccurrences(of: "_", with: "", options: .literal, range: NSRange(location: startItalic, length: endItalic - startItalic + (countEmoji + 1)))
  537. } else {
  538. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(startItalic + countEmoji...startItalic + countEmoji))
  539. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(endItalic + countEmoji...endItalic + countEmoji))
  540. }
  541. boolItalic = false
  542. startItalic = 0
  543. } else {
  544. startItalic = i
  545. startItalic = startItalic - (textUTF8.count - finalText.string.count)
  546. }
  547. }
  548. //UNDERLINE
  549. if String(textUTF8.substring(from: i, to: i)) == "^" && !boolUnderLine {
  550. boolUnderLine = true
  551. startUnderLine = i
  552. startUnderLine = startUnderLine - (textUTF8.count - finalText.string.count)
  553. } else if String(textUTF8.substring(from: i, to: i)) == "^" && boolUnderLine {
  554. endUnderLine = i
  555. endUnderLine = endUnderLine - (textUTF8.count - finalText.string.count)
  556. if (startUnderLine - 1 == -1 || checkCharBeforeAfter(char: String(finalText.string.substring(from: startUnderLine - 1, to: startUnderLine - 1)))) && (endUnderLine + 1 == finalText.string.count || checkCharBeforeAfter(char: String(finalText.string.substring(from: endUnderLine + 1, to: endUnderLine + 1)))) && endUnderLine - startUnderLine != 1 && !String(finalText.string.substring(from: startUnderLine + 1, to: endUnderLine - 1)).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
  557. let countEmoji = finalText.string.countEmojiCharacter()
  558. finalText.addAttribute(.underlineStyle, value: NSUnderlineStyle.thick.rawValue, range: NSRange(location: startUnderLine, length: endUnderLine - startUnderLine + (countEmoji + 1)))
  559. if !isEditing{
  560. finalText.mutableString.replaceOccurrences(of: "^", with: "", options: .literal, range: NSRange(location: startUnderLine, length: endUnderLine - startUnderLine + (countEmoji + 1)))
  561. } else {
  562. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(startUnderLine + countEmoji...startUnderLine + countEmoji))
  563. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(endUnderLine + countEmoji...endUnderLine + countEmoji))
  564. }
  565. boolUnderLine = false
  566. startUnderLine = 0
  567. } else {
  568. startUnderLine = i
  569. startUnderLine = startUnderLine - (textUTF8.count - finalText.string.count)
  570. }
  571. }
  572. //STRIKETHROUGH
  573. if String(textUTF8.substring(from: i, to: i)) == "~" && !boolStrike {
  574. boolStrike = true
  575. startStrike = i
  576. startStrike = startStrike - (textUTF8.count - finalText.string.count)
  577. } else if String(textUTF8.substring(from: i, to: i)) == "~" && boolStrike {
  578. endStrike = i
  579. endStrike = endStrike - (textUTF8.count - finalText.string.count)
  580. if (startStrike - 1 == -1 || checkCharBeforeAfter(char: String(finalText.string.substring(from: startStrike - 1, to: startStrike - 1)))) && (endStrike + 1 == finalText.string.count || checkCharBeforeAfter(char: String(finalText.string.substring(from: endStrike + 1, to: endStrike + 1)))) && endStrike - startStrike != 1 && !String(finalText.string.substring(from: startStrike + 1, to: endStrike - 1)).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
  581. let countEmoji = finalText.string.countEmojiCharacter()
  582. finalText.addAttribute(.strikethroughStyle, value: 2, range: NSRange(location: startStrike, length: endStrike - startStrike + (countEmoji + 1)))
  583. if !isEditing {
  584. finalText.mutableString.replaceOccurrences(of: "~", with: "", options: .literal, range: NSRange(location: startStrike, length: endStrike - startStrike + (countEmoji + 1)))
  585. } else {
  586. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(startStrike + countEmoji...startStrike + countEmoji))
  587. finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(endStrike + countEmoji...endStrike + countEmoji))
  588. }
  589. boolStrike = false
  590. startStrike = 0
  591. } else {
  592. startStrike = i
  593. startStrike = startStrike - (textUTF8.count - finalText.string.count)
  594. }
  595. }
  596. }
  597. if !isEditing {
  598. let listText = finalText.string.split(separator: " ")
  599. for i in 0...listText.count - 1 {
  600. if listText[i].lowercased().checkStartWithLink() {
  601. if ((listText[i].lowercased().starts(with: "www.") && listText[i].lowercased().split(separator: ".").count >= 3) || (!listText[i].lowercased().starts(with: "www.") && listText[i].lowercased().split(separator: ".").count >= 2)) && listText[i].lowercased().split(separator: ".").last!.count >= 2 {
  602. if let range: Range<String.Index> = finalText.string.range(of: listText[i]) {
  603. let index: Int = finalText.string.distance(from: finalText.string.startIndex, to: range.lowerBound)
  604. finalText.addAttribute(.foregroundColor, value: UIColor.linkColor, range: NSRange(index...index + listText[i].count - 1))
  605. finalText.addAttribute(.underlineStyle, value: NSUnderlineStyle.thick.rawValue, range: NSRange(index...index + listText[i].count - 1))
  606. }
  607. }
  608. }
  609. }
  610. }
  611. if isSearching {
  612. let attributedText = NSMutableAttributedString(string: finalText.string) // 1
  613. let range = NSString(string: finalText.string).range(of: textSearch, options: .caseInsensitive) // 2
  614. let highlightColor = UIColor.systemYellow // 3
  615. let highlightedAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.backgroundColor: highlightColor] // 4
  616. finalText.addAttributes(highlightedAttributes, range: range) // 5
  617. }
  618. }
  619. return finalText
  620. }
  621. func checkCharBeforeAfter(char: String) -> Bool {
  622. return char == " " || char == "\n" || char == "*" || char == "_" || char == "^" || char == "~"
  623. }
  624. func checkStartWithLink() -> Bool {
  625. return self.starts(with: "https://") || self.starts(with: "http://") || self.starts(with: "www.")
  626. //|| self.starts(with: "*https://") || self.starts(with: "*http://") || self.starts(with: "*www.") || self.starts(with: "_https://") || self.starts(with: "_http://") || self.starts(with: "_www.") || self.starts(with: "^https://") || self.starts(with: "^http://") || self.starts(with: "^www.") || self.starts(with: "~https://") || self.starts(with: "~http://") || self.starts(with: "~www.")
  627. }
  628. }
  629. extension UIFont {
  630. var bold: UIFont {
  631. return with(traits: .traitBold)
  632. } // bold
  633. var italic: UIFont {
  634. return with(traits: .traitItalic)
  635. } // italic
  636. func with(traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
  637. guard let descriptor = self.fontDescriptor.withSymbolicTraits(traits) else {
  638. return self
  639. } // guard
  640. return UIFont(descriptor: descriptor, size: 0)
  641. } // with(traits:)
  642. }
  643. extension UILabel {
  644. public func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat) {
  645. let attachment = NSTextAttachment()
  646. attachment.image = image
  647. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  648. let attachmentStr = NSAttributedString(attachment: attachment)
  649. let mutableAttributedString = NSMutableAttributedString()
  650. mutableAttributedString.append(attachmentStr)
  651. let textString = NSAttributedString(string: text, attributes: [.font: self.font!])
  652. mutableAttributedString.append(textString)
  653. self.attributedText = mutableAttributedString
  654. }
  655. public func setAttributeText(image: UIImage, with textMutable: NSAttributedString, size: CGFloat, y: CGFloat) {
  656. let attachment = NSTextAttachment()
  657. attachment.image = image
  658. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  659. let attachmentStr = NSAttributedString(attachment: attachment)
  660. let mutableAttributedString = NSMutableAttributedString()
  661. mutableAttributedString.append(attachmentStr)
  662. mutableAttributedString.append(textMutable)
  663. self.attributedText = mutableAttributedString
  664. }
  665. }
  666. extension Bundle {
  667. public static func resourceBundle(for frameworkClass: AnyClass) -> Bundle {
  668. guard let moduleName = String(reflecting: frameworkClass).components(separatedBy: ".").first else {
  669. fatalError("Couldn't determine module name from class \(frameworkClass)")
  670. }
  671. let frameworkBundle = Bundle(for: frameworkClass)
  672. guard let resourceBundleURL = frameworkBundle.url(forResource: "NexilisLite", withExtension: "bundle"),
  673. let resourceBundle = Bundle(url: resourceBundleURL) else {
  674. print("\(moduleName).bundle not found in \(frameworkBundle)")
  675. return frameworkBundle
  676. }
  677. return resourceBundle
  678. }
  679. }
  680. //extension UIFont {
  681. //
  682. // static func register(from url: URL) throws {
  683. // guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
  684. // throw fatalError("Could not create font data provider for \(url).")
  685. // }
  686. // let font = CGFont(fontDataProvider)
  687. // var error: Unmanaged<CFError>?
  688. // guard CTFontManagerRegisterGraphicsFont(font!, &error) else {
  689. // throw error!.takeUnretainedValue()
  690. // }
  691. // }
  692. //
  693. //}
  694. extension UIButton {
  695. private func actionHandleBlock(action:(() -> Void)? = nil) {
  696. struct __ {
  697. static var action :(() -> Void)?
  698. }
  699. if action != nil {
  700. __.action = action
  701. } else {
  702. __.action?()
  703. }
  704. }
  705. @objc private func triggerActionHandleBlock() {
  706. self.actionHandleBlock()
  707. }
  708. public func actionHandle(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
  709. self.actionHandleBlock(action: action)
  710. self.addTarget(self, action: #selector(self.triggerActionHandleBlock), for: control)
  711. }
  712. }
  713. extension UINavigationController {
  714. func replaceAllViewController(with viewController: UIViewController, animated: Bool) {
  715. pushViewController(viewController, animated: animated)
  716. viewControllers.removeSubrange(1...viewControllers.count - 2)
  717. }
  718. var rootViewController : UIViewController? {
  719. return viewControllers.first
  720. }
  721. }
  722. extension UIImageView {
  723. private static var taskKey = 0
  724. private static var urlKey = 0
  725. private var currentTask: URLSessionTask? {
  726. get { return objc_getAssociatedObject(self, &UIImageView.taskKey) as? URLSessionTask }
  727. set { objc_setAssociatedObject(self, &UIImageView.taskKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
  728. }
  729. private var currentURL: URL? {
  730. get { return objc_getAssociatedObject(self, &UIImageView.urlKey) as? URL }
  731. set { objc_setAssociatedObject(self, &UIImageView.urlKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
  732. }
  733. func loadImageAsync(with urlString: String?) {
  734. // cancel prior task, if any
  735. weak var oldTask = currentTask
  736. currentTask = nil
  737. oldTask?.cancel()
  738. // reset imageview's image
  739. self.image = nil
  740. // allow supplying of `nil` to remove old image and then return immediately
  741. guard let urlString = urlString else { return }
  742. // check cache
  743. if let cachedImage = ImageCache.shared.image(forKey: urlString) {
  744. self.image = cachedImage
  745. return
  746. }
  747. // download
  748. let url = URL(string: urlString)!
  749. currentURL = url
  750. let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
  751. self?.currentTask = nil
  752. //error handling
  753. if let error = error {
  754. // don't bother reporting cancelation errors
  755. if (error as NSError).domain == NSURLErrorDomain && (error as NSError).code == NSURLErrorCancelled {
  756. return
  757. }
  758. print(error)
  759. return
  760. }
  761. guard let data = data, let downloadedImage = UIImage(data: data) else {
  762. print("unable to extract image")
  763. return
  764. }
  765. ImageCache.shared.save(image: downloadedImage, forKey: urlString)
  766. if url == self?.currentURL {
  767. DispatchQueue.main.async {
  768. self?.image = downloadedImage
  769. }
  770. }
  771. }
  772. // save and start new task
  773. currentTask = task
  774. task.resume()
  775. }
  776. private func actionHandleBlock(action:(() -> Void)? = nil) {
  777. struct __ {
  778. static var action :(() -> Void)?
  779. }
  780. if action != nil {
  781. __.action = action
  782. } else {
  783. __.action?()
  784. }
  785. }
  786. @objc private func triggerActionHandleBlock() {
  787. self.actionHandleBlock()
  788. }
  789. func actionHandle(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
  790. self.actionHandleBlock(action: action)
  791. self.isUserInteractionEnabled = true
  792. self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.triggerActionHandleBlock)))
  793. }
  794. }
  795. extension UITextField {
  796. public enum PaddingSide {
  797. case left(CGFloat)
  798. case right(CGFloat)
  799. case both(CGFloat)
  800. }
  801. public func addPadding(_ padding: PaddingSide) {
  802. self.leftViewMode = .always
  803. self.layer.masksToBounds = true
  804. switch padding {
  805. case .left(let spacing):
  806. let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
  807. self.leftView = paddingView
  808. self.rightViewMode = .always
  809. case .right(let spacing):
  810. let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
  811. self.rightView = paddingView
  812. self.rightViewMode = .always
  813. case .both(let spacing):
  814. let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
  815. // left
  816. self.leftView = paddingView
  817. self.leftViewMode = .always
  818. // right
  819. self.rightView = paddingView
  820. self.rightViewMode = .always
  821. }
  822. }
  823. }
  824. class ImageCache {
  825. private let cache = NSCache<NSString, UIImage>()
  826. private var observer: NSObjectProtocol!
  827. static let shared = ImageCache()
  828. private init() {
  829. // make sure to purge cache on memory pressure
  830. observer = NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: nil) { [weak self] notification in
  831. self?.cache.removeAllObjects()
  832. }
  833. }
  834. deinit {
  835. NotificationCenter.default.removeObserver(observer as Any)
  836. }
  837. func image(forKey key: String) -> UIImage? {
  838. return cache.object(forKey: key as NSString)
  839. }
  840. func save(image: UIImage, forKey key: String) {
  841. cache.setObject(image, forKey: key as NSString)
  842. }
  843. }
  844. //let alert = UIAlertController(title: "", message: "\n\n\n\n\n\n\n\n\n\n".localized(), preferredStyle: .alert)
  845. //let newWidth = UIScreen.main.bounds.width * 0.90 - 270
  846. //// update width constraint value for main view
  847. //if let viewWidthConstraint = alert.view.constraints.filter({ return $0.firstAttribute == .width }).first{
  848. // viewWidthConstraint.constant = newWidth
  849. //}
  850. //// update width constraint value for container view
  851. //if let containerViewWidthConstraint = alert.view.subviews.first?.constraints.filter({ return $0.firstAttribute == .width }).first {
  852. // containerViewWidthConstraint.constant = newWidth
  853. //}
  854. //let titleFont = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18), NSAttributedString.Key.foregroundColor: UIColor.black]
  855. //let titleAttrString = NSMutableAttributedString(string: m["MERNAM"]!.localized(), attributes: titleFont)
  856. //alert.setValue(titleAttrString, forKey: "attributedTitle")
  857. //alert.view.subviews.first?.subviews.first?.subviews.first?.backgroundColor = .lightGray
  858. //alert.view.tintColor = .black
  859. //if fileType != BroadcastViewController.FILE_TYPE_CHAT{
  860. // let height:NSLayoutConstraint = NSLayoutConstraint(item: alert.view!, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: UIScreen.main.bounds.height * 0.6)
  861. // alert.view.addConstraint(height)
  862. //}
  863. //
  864. //var containerView = UIView(frame: CGRect(x: 20, y: 60, width: alert.view.bounds.size.width * 0.9 - 40, height: 350))
  865. //if fileType == BroadcastViewController.FILE_TYPE_CHAT {
  866. // containerView = UIView(frame: CGRect(x: 20, y: 60, width: alert.view.bounds.size.width * 0.9 - 40, height: 100))
  867. //}
  868. //alert.view.addSubview(containerView)
  869. //containerView.layer.cornerRadius = 10.0
  870. //containerView.clipsToBounds = true
  871. //
  872. //let buttonClose = UIButton(type: .close)
  873. //buttonClose.frame = CGRect(x: alert.view.bounds.size.width * 0.9 - 50, y: 15, width: 30, height: 30)
  874. //buttonClose.layer.cornerRadius = 15.0
  875. //buttonClose.clipsToBounds = true
  876. //buttonClose.backgroundColor = .secondaryColor.withAlphaComponent(0.5)
  877. //buttonClose.actionHandle(controlEvents: .touchUpInside,
  878. // ForAction:{() -> Void in
  879. // alert.dismiss(animated: true, completion: nil)
  880. // })
  881. //alert.view.addSubview(buttonClose)
  882. //
  883. //let titleBroadcast = UILabel()
  884. //containerView.addSubview(titleBroadcast)
  885. //titleBroadcast.translatesAutoresizingMaskIntoConstraints = false
  886. //NSLayoutConstraint.activate([
  887. // titleBroadcast.topAnchor.constraint(equalTo: containerView.topAnchor),
  888. // titleBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
  889. // titleBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
  890. //])
  891. //titleBroadcast.font = UIFont.systemFont(ofSize: 18)
  892. //titleBroadcast.numberOfLines = 0
  893. //titleBroadcast.text = m[CoreMessage_TMessageKey.TITLE]
  894. //titleBroadcast.textColor = .black
  895. //
  896. //let descBroadcast = UILabel()
  897. //containerView.addSubview(descBroadcast)
  898. //descBroadcast.translatesAutoresizingMaskIntoConstraints = false
  899. //NSLayoutConstraint.activate([
  900. // descBroadcast.topAnchor.constraint(equalTo: titleBroadcast.bottomAnchor, constant: 10),
  901. // descBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
  902. // descBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
  903. //])
  904. //descBroadcast.font = UIFont.systemFont(ofSize: 15)
  905. //descBroadcast.numberOfLines = 0
  906. //descBroadcast.text = m[CoreMessage_TMessageKey.MESSAGE_TEXT_ENG]
  907. //descBroadcast.textColor = .black
  908. //
  909. //let stringLink = m[CoreMessage_TMessageKey.LINK] ?? ""
  910. //let linkBroadcast = UILabel()
  911. //if !stringLink.isEmpty {
  912. // containerView.addSubview(linkBroadcast)
  913. // linkBroadcast.translatesAutoresizingMaskIntoConstraints = false
  914. // NSLayoutConstraint.activate([
  915. // linkBroadcast.topAnchor.constraint(equalTo: descBroadcast.bottomAnchor, constant: 10),
  916. // linkBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
  917. // linkBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
  918. // ])
  919. // linkBroadcast.font = UIFont.systemFont(ofSize: 15)
  920. // linkBroadcast.isUserInteractionEnabled = true
  921. // linkBroadcast.numberOfLines = 0
  922. // let attributedString = NSMutableAttributedString(string: stringLink, attributes:[NSAttributedString.Key.link: URL(string: stringLink)!])
  923. // linkBroadcast.attributedText = attributedString
  924. // let tap = ObjectGesture(target: self, action: #selector(tapLinkBroadcast))
  925. // tap.message_id = stringLink
  926. // linkBroadcast.addGestureRecognizer(tap)
  927. //}
  928. //
  929. //let dottedLine = UIView()
  930. //containerView.addSubview(dottedLine)
  931. //dottedLine.translatesAutoresizingMaskIntoConstraints = false
  932. //var constraintDottedLine = dottedLine.topAnchor.constraint(equalTo: descBroadcast.bottomAnchor, constant: 20)
  933. //if !stringLink.isEmpty{
  934. // constraintDottedLine = dottedLine.topAnchor.constraint(equalTo: linkBroadcast.bottomAnchor, constant: 20)
  935. //}
  936. //NSLayoutConstraint.activate([
  937. // constraintDottedLine,
  938. // dottedLine.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
  939. // dottedLine.widthAnchor.constraint(equalToConstant: alert.view.bounds.size.width * 0.9 - 40),
  940. // dottedLine.heightAnchor.constraint(equalToConstant: 2)
  941. //])
  942. //dottedLine.backgroundColor = .black.withAlphaComponent(0.1)
  943. //let shapeLayer = CAShapeLayer()
  944. //shapeLayer.strokeColor = UIColor.black.withAlphaComponent(0.2).cgColor
  945. //shapeLayer.lineWidth = 2
  946. //// passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
  947. //shapeLayer.lineDashPattern = [2,3]
  948. //
  949. //let path = CGMutablePath()
  950. //path.addLines(between: [CGPoint(x: 0, y: 0),
  951. // CGPoint(x: alert.view.bounds.size.width * 0.9 - 20, y: 0)])
  952. //shapeLayer.path = path
  953. //dottedLine.layer.addSublayer(shapeLayer)
  954. //
  955. //let thumb = m[CoreMessage_TMessageKey.THUMB_ID] ?? ""
  956. //let image = m[CoreMessage_TMessageKey.IMAGE_ID] ?? ""
  957. //let video = m[CoreMessage_TMessageKey.VIDEO_ID] ?? ""
  958. //let file = m[CoreMessage_TMessageKey.FILE_ID] ?? ""
  959. //if fileType != BroadcastViewController.FILE_TYPE_CHAT {
  960. // let imageBroadcast = UIImageView()
  961. // containerView.addSubview(imageBroadcast)
  962. // imageBroadcast.translatesAutoresizingMaskIntoConstraints = false
  963. // NSLayoutConstraint.activate([
  964. // imageBroadcast.topAnchor.constraint(equalTo: dottedLine.bottomAnchor, constant: 20),
  965. // imageBroadcast.widthAnchor.constraint(equalToConstant: alert.view.bounds.size.width * 0.9 - 40),
  966. // imageBroadcast.heightAnchor.constraint(equalToConstant: 250)
  967. // ])
  968. // imageBroadcast.layer.cornerRadius = 10.0
  969. // imageBroadcast.clipsToBounds = true
  970. // if fileType != BroadcastViewController.FILE_TYPE_DOCUMENT {
  971. // imageBroadcast.contentMode = .scaleAspectFill
  972. // imageBroadcast.setImage(name: thumb)
  973. //
  974. // if fileType == BroadcastViewController.FILE_TYPE_VIDEO {
  975. // let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  976. // imageBroadcast.addSubview(imagePlay)
  977. // imagePlay.clipsToBounds = true
  978. // imagePlay.translatesAutoresizingMaskIntoConstraints = false
  979. // imagePlay.centerYAnchor.constraint(equalTo: imageBroadcast.centerYAnchor).isActive = true
  980. // imagePlay.centerXAnchor.constraint(equalTo: imageBroadcast.centerXAnchor).isActive = true
  981. // imagePlay.widthAnchor.constraint(equalToConstant: 60).isActive = true
  982. // imagePlay.heightAnchor.constraint(equalToConstant: 60).isActive = true
  983. // imagePlay.tintColor = .gray.withAlphaComponent(0.5)
  984. // }
  985. // } else {
  986. // imageBroadcast.image = UIImage(systemName: "doc.fill")
  987. // imageBroadcast.tintColor = .mainColor
  988. // imageBroadcast.contentMode = .scaleAspectFit
  989. // }
  990. //
  991. // imageBroadcast.actionHandle(controlEvents: .touchUpInside,
  992. // ForAction:{() -> Void in
  993. // let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  994. // let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  995. // let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  996. // if fileType == BroadcastViewController.FILE_TYPE_IMAGE {
  997. // if let dirPath = paths.first {
  998. // let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(image)
  999. // if FileManager.default.fileExists(atPath: imageURL.path) {
  1000. // let image = UIImage(contentsOfFile: imageURL.path)
  1001. // let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  1002. // previewImageVC.image = image
  1003. // previewImageVC.isHiddenTextField = true
  1004. // previewImageVC.modalPresentationStyle = .overFullScreen
  1005. // previewImageVC.modalTransitionStyle = .crossDissolve
  1006. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1007. // if checkViewController != nil {
  1008. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(previewImageVC, animated: true, completion: nil)
  1009. // } else {
  1010. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(previewImageVC, animated: true, completion: nil)
  1011. // }
  1012. // } else {
  1013. // Download().start(forKey: image) { (name, progress) in
  1014. // guard progress == 100 else {
  1015. // return
  1016. // }
  1017. //
  1018. // DispatchQueue.main.async {
  1019. // let image = UIImage(contentsOfFile: imageURL.path)
  1020. // let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  1021. // previewImageVC.image = image
  1022. // previewImageVC.isHiddenTextField = true
  1023. // previewImageVC.modalPresentationStyle = .overFullScreen
  1024. // previewImageVC.modalTransitionStyle = .crossDissolve
  1025. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1026. // if checkViewController != nil {
  1027. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(previewImageVC, animated: true, completion: nil)
  1028. // } else {
  1029. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(previewImageVC, animated: true, completion: nil)
  1030. // }
  1031. // }
  1032. // }
  1033. // }
  1034. // }
  1035. // } else if fileType == BroadcastViewController.FILE_TYPE_VIDEO {
  1036. // if let dirPath = paths.first {
  1037. // let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(video)
  1038. // if FileManager.default.fileExists(atPath: videoURL.path) {
  1039. // let player = AVPlayer(url: videoURL as URL)
  1040. // let playerVC = AVPlayerViewController()
  1041. // playerVC.player = player
  1042. // playerVC.modalPresentationStyle = .custom
  1043. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1044. // if checkViewController != nil {
  1045. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(playerVC, animated: true, completion: nil)
  1046. // } else {
  1047. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(playerVC, animated: true, completion: nil)
  1048. // }
  1049. // } else {
  1050. // Download().start(forKey: video) { (name, progress) in
  1051. // DispatchQueue.main.async {
  1052. // guard progress == 100 else {
  1053. // return
  1054. // }
  1055. // let player = AVPlayer(url: videoURL as URL)
  1056. // let playerVC = AVPlayerViewController()
  1057. // playerVC.player = player
  1058. // playerVC.modalPresentationStyle = .custom
  1059. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1060. // if checkViewController != nil {
  1061. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(playerVC, animated: true, completion: nil)
  1062. // } else {
  1063. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(playerVC, animated: true, completion: nil)
  1064. // }
  1065. // }
  1066. // }
  1067. // }
  1068. // }
  1069. // } else if fileType == BroadcastViewController.FILE_TYPE_DOCUMENT {
  1070. // if let dirPath = paths.first {
  1071. // let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  1072. // if FileManager.default.fileExists(atPath: fileURL.path) {
  1073. // previewItem = fileURL as NSURL
  1074. // let previewController = QLPreviewController()
  1075. // let rightBarButton = UIBarButtonItem()
  1076. // previewController.navigationItem.rightBarButtonItem = rightBarButton
  1077. // previewController.dataSource = self
  1078. // previewController.modalPresentationStyle = .overFullScreen
  1079. //
  1080. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1081. // if checkViewController != nil {
  1082. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.show(previewController, sender: nil)
  1083. // } else {
  1084. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.show(previewController, sender: nil)
  1085. // }
  1086. // } else {
  1087. // Download().start(forKey: file) { (name, progress) in
  1088. // DispatchQueue.main.async {
  1089. // guard progress == 100 else {
  1090. // return
  1091. // }
  1092. // previewItem = fileURL as NSURL
  1093. // let previewController = QLPreviewController()
  1094. // let rightBarButton = UIBarButtonItem()
  1095. // previewController.navigationItem.rightBarButtonItem = rightBarButton
  1096. // previewController.dataSource = self
  1097. // previewController.modalPresentationStyle = .overFullScreen
  1098. //
  1099. // let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1100. // if checkViewController != nil {
  1101. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.show(previewController, sender: nil)
  1102. // } else {
  1103. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.show(previewController, sender: nil)
  1104. // }
  1105. // }
  1106. // }
  1107. // }
  1108. // }
  1109. // }
  1110. // })
  1111. //}
  1112. //
  1113. //let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
  1114. //if checkViewController != nil {
  1115. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(alert, animated: true, completion: nil)
  1116. //} else {
  1117. // UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(alert, animated: true, completion: nil)
  1118. //}