Repository: EasyIOS/EasyIOS-Swift Branch: master Commit: b269aff08a07 Files: 154 Total size: 935.2 KB Directory structure: gitextract_b4h_akqo/ ├── .gitignore ├── .travis.yml ├── Demo/ │ ├── Demo/ │ │ ├── AppDelegate.swift │ │ ├── Base.lproj/ │ │ │ └── LaunchScreen.xib │ │ ├── CollectionCellViewModel.swift │ │ ├── CollectionScene.swift │ │ ├── CollectionSceneModel.swift │ │ ├── EncodeLoginScene.swift │ │ ├── FeedRequest.swift │ │ ├── FlexScene.swift │ │ ├── FlexXmlScene.swift │ │ ├── Images.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LabelScene.swift │ │ ├── LoginScene.swift │ │ ├── MainLabelDeleage.swift │ │ ├── MainScene.swift │ │ ├── MainSceneModel.swift │ │ ├── url.plist │ │ └── xml.bundle/ │ │ ├── CollectionScene.xml │ │ ├── EncodeLoginScene.crypto │ │ ├── FlexXmlScene.xml │ │ ├── LabelHtml.xml │ │ ├── LabelScene.xml │ │ ├── LoginScene.xml │ │ └── MainScene.xml │ ├── Demo.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ ├── Demo.xcworkspace/ │ │ └── contents.xcworkspacedata │ ├── DemoTests/ │ │ ├── DemoTests.swift │ │ └── Info.plist │ └── Podfile ├── EasyIOS-Swift.podspec ├── LICENSE ├── Pod/ │ ├── Assets/ │ │ └── .gitkeep │ └── Classes/ │ ├── .gitkeep │ ├── Easy/ │ │ ├── Core/ │ │ │ ├── EZAction.swift │ │ │ ├── EZCollectionView.swift │ │ │ ├── EZNavigationController.swift │ │ │ ├── EZRequest.swift │ │ │ ├── EZScene.swift │ │ │ ├── EZSceneModel.swift │ │ │ ├── EZTableView.swift │ │ │ └── EZViewModel.swift │ │ └── Lib/ │ │ ├── EZCoreDataManager.swift │ │ ├── EZExtend+Array.swift │ │ ├── EZExtend+Bond.swift │ │ ├── EZExtend+Dictionary.swift │ │ ├── EZExtend+NSDate.swift │ │ ├── EZExtend+String.swift │ │ ├── EZExtend+UIAlertController.swift │ │ ├── EZExtend+UIButton.swift │ │ ├── EZExtend+UIFont.swift │ │ ├── EZExtend+UIImage.swift │ │ ├── EZExtend+UILabel.swift │ │ ├── EZExtend+UIView.swift │ │ ├── EZExtend+UIViewController.swift │ │ ├── EZKit.swift │ │ ├── EZPrintln.swift │ │ ├── EZSystemInfo.swift │ │ ├── EZWatch.swift │ │ ├── EasyORM.swift │ │ ├── NSData+EasyExtend.h │ │ ├── NSData+EasyExtend.m │ │ └── PullRefresh/ │ │ ├── EZInfiniteScrolling.swift │ │ ├── EZPullToRefresh.swift │ │ ├── PullFooter.swift │ │ └── PullHeader.swift │ ├── Extend/ │ │ ├── EUI/ │ │ │ ├── Defines.h │ │ │ ├── DesEncrypt.h │ │ │ ├── DesEncrypt.m │ │ │ ├── ENSObject.swift │ │ │ ├── EUI+ButtonProperty.swift │ │ │ ├── EUI+CollectionViewProperty.swift │ │ │ ├── EUI+ImageProperty.swift │ │ │ ├── EUI+LabelProperty.swift │ │ │ ├── EUI+ScrollViewProperty.swift │ │ │ ├── EUI+TableViewProperty.swift │ │ │ ├── EUI+TextFieldProperty.swift │ │ │ ├── EUI+ViewProperty.swift │ │ │ ├── EUI.swift │ │ │ ├── EUIExtend+UIView.swift │ │ │ ├── EUIKit.swift │ │ │ ├── EUIParse.swift │ │ │ ├── EUIProperty.swift │ │ │ ├── EUScene.swift │ │ │ ├── EZJSCore.swift │ │ │ ├── NSAttributedString+HTMLStyle.h │ │ │ ├── NSAttributedString+HTMLStyle.m │ │ │ ├── StringFormat.swift │ │ │ ├── SwiftTryCatch.h │ │ │ ├── SwiftTryCatch.m │ │ │ ├── UIColor+HTMLColors.h │ │ │ └── UIColor+HTMLColors.m │ │ ├── FlexboxKit/ │ │ │ ├── FLEXBOXContainerView.h │ │ │ ├── FLEXBOXContainerView.m │ │ │ ├── FLEXBOXNode.h │ │ │ ├── FLEXBOXNode.m │ │ │ ├── FlexboxKit.h │ │ │ ├── Layout.c │ │ │ ├── Layout.h │ │ │ ├── UIView+FLEXBOX.h │ │ │ └── UIView+FLEXBOX.m │ │ ├── Pinyin/ │ │ │ ├── pinyin.h │ │ │ └── pinyin.m │ │ ├── SwiftRegex/ │ │ │ ├── Match.swift │ │ │ └── Regex.swift │ │ └── URLManager/ │ │ ├── UIViewController+URLManage.h │ │ ├── UIViewController+URLManage.m │ │ ├── URLManager.h │ │ ├── URLManager.m │ │ ├── URLNavigation.h │ │ └── URLNavigation.m │ └── Private/ │ ├── Gumbo/ │ │ ├── attribute.c │ │ ├── attribute.h │ │ ├── char_ref.c │ │ ├── char_ref.h │ │ ├── error.c │ │ ├── error.h │ │ ├── gumbo.h │ │ ├── insertion_mode.h │ │ ├── parser.c │ │ ├── parser.h │ │ ├── string_buffer.c │ │ ├── string_buffer.h │ │ ├── string_piece.c │ │ ├── string_piece.h │ │ ├── tag.c │ │ ├── token_type.h │ │ ├── tokenizer.c │ │ ├── tokenizer.h │ │ ├── tokenizer_states.h │ │ ├── utf8.c │ │ ├── utf8.h │ │ ├── util.c │ │ ├── util.h │ │ ├── vector.c │ │ └── vector.h │ └── ObjectiveGumbo/ │ ├── NSString+OGString.h │ ├── NSString+OGString.m │ ├── OGDocument.h │ ├── OGDocument.m │ ├── OGElement.h │ ├── OGElement.m │ ├── OGNode.h │ ├── OGNode.m │ ├── OGText.h │ ├── OGText.m │ ├── OGUtility.h │ ├── OGUtility.m │ ├── ObjectiveGumbo.h │ └── ObjectiveGumbo.m ├── README/ │ └── Chinese.md └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # OS X .DS_Store # Xcode build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout profile *.moved-aside DerivedData *.hmap *.ipa # Bundler .bundle Demo/Pods/ # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # # Note: if you ignore the Pods directory, make sure to uncomment # `pod install` in .travis.yml # Demo/Pods/ ================================================ FILE: .travis.yml ================================================ # references: # * http://www.objc.io/issue-6/travis-ci.html # * https://github.com/supermarin/xcpretty#usage osx_image: xcode7 language: objective-c cache: cocoapods podfile: Demo/Podfile before_install: - gem install cocoapods # Since Travis is not always on latest version - pod install --project-directory=Demo install: - gem install xcpretty --no-rdoc --no-ri --no-document --quiet script: - set -o pipefail && xcodebuild test -workspace Demo/Demo.xcworkspace -scheme EasyIOS-Swift -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c - pod lib lint --quick notifications:   webhooks: https://hooks.pubu.im/services/21x4lqp4vepnfbn ================================================ FILE: Demo/Demo/AppDelegate.swift ================================================ // // AppDelegate.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { CODE_KEY = "code"//错误码 MSG_KEY = "msg" //网络请求的msg信息 LIVE_LOAD_PATH = NSURL(fileURLWithPath: __FILE__).URLByDeletingLastPathComponent!.path!+"/xml.bundle" //xml文件的路径(项目路径) CRTPTO_KEY = "easyios" //这是自定义的密钥 URLManager.loadConfigFromPlist(NSBundle.mainBundle().pathForResource("url", ofType: "plist")) // 初始化URLManager let firstVc = MainScene() let nav = EZNavigationController(rootViewController: firstVc) self.window = UIWindow(frame: UIScreen.mainScreen().bounds) self.window!.rootViewController = nav self.window!.backgroundColor = UIColor.whiteColor() self.window!.makeKeyAndVisible() // Override point for customization after application launch. return true } func applicationWillResignActive(application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } func applicationDidEnterBackground(application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } ================================================ FILE: Demo/Demo/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: Demo/Demo/CollectionCellViewModel.swift ================================================ // // CollectionCellViewModel.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS class CollectionCellViewModel: EZViewModel { var url:EZString? var name:EZString? init(url:String?,name:String?){ if url != nil { self.url = EZString(url!) } if name != nil { self.name = EZString(name!) } } } ================================================ FILE: Demo/Demo/CollectionScene.swift ================================================ // // CollectionScene.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS import Bond import SVProgressHUD class CollectionScene: EUScene,UICollectionViewDelegate { var sceneModel = CollectionSceneModel() var collectionView:UICollectionView? override func viewDidLoad() { super.viewDidLoad() self.showBarButton(.LEFT, title: "返回", fontColor: UIColor.greenColor()) self.sceneModel.req.requestNeedActive.value = true self.sceneModel.req.state.observe{ switch $0 { case .Sending : SVProgressHUD.show() case .Success,.SuccessFromCache : SVProgressHUD.dismiss() self.collectionView?.pullToRefreshView?.stopAnimating() case .Error : SVProgressHUD.showErrorWithStatus("数据加载失败") default : return } } // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func eu_collectionViewDidLoad(collectionView: UICollectionView?) { self.collectionView = collectionView collectionView?.delegate = self define("handlePullRefresh"){ self.sceneModel.req.requestNeedActive.value = true } define("handleInfinite"){ let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC))) dispatch_after(delayTime, dispatch_get_main_queue()) { self.collectionView?.infiniteScrollingView?.stopAnimating() self.collectionView?.infiniteScrollingView?.setEnded() } } self.sceneModel.viewModelList.lift().bindTo(collectionView!) { (indexPath, dataArray, collectionView) -> UICollectionViewCell in collectionView.dequeueReusableCell("cell", forIndexPath: indexPath, target: self, bind: dataArray[indexPath.section][indexPath.row]) } } override func leftButtonTouch() { URLNavigation.dismissCurrentAnimated(true) } } ================================================ FILE: Demo/Demo/CollectionSceneModel.swift ================================================ // // CollectionCellViewModel.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS import Bond import ObjectMapper class FeedModel :Mappable{ var url:String? var name:String? required init?(_ map: Map){ } func mapping(map: Map) { url <- map["url"] name <- map["name"] } } class FeedList:Mappable { var list:[FeedModel]? required init?(_ map: Map){ } func mapping(map: Map) { list <- map["list"] } } class CollectionSceneModel: EZSceneModel { var req = FeedRequest() var modelList:FeedList? var viewModelList = ObservableArray([CollectionCellViewModel]()) override init (){ super.init() self.req.requestBlock = { EZAction.SEND_IQ_CACHE(self.req) } self.req.state.observe{[unowned self] value in switch value { case .Success,.SuccessFromCache : if let theData: AnyObject = self.req.output["data"] { self.modelList = Mapper().map(theData) if let array = self.modelList?.list?.map({ (model) -> CollectionCellViewModel in return CollectionCellViewModel(url: model.url, name: model.name) }) { self.viewModelList.removeAll() self.viewModelList.extend(array) } } default : return } } } } ================================================ FILE: Demo/Demo/EncodeLoginScene.swift ================================================ // // EncodeLoginScene.swift // Demo // // Created by zhuchao on 15/5/26. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import EasyIOS class EncodeLoginScene: EUScene { override func loadView() { self.SUFFIX = "crypto" //修改后缀为加密版本的 super.loadView() } override func viewDidLoad() { super.viewDidLoad() self.showBarButton(.LEFT, title: "返回", fontColor: UIColor.greenColor()) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func leftButtonTouch() { URLNavigation.dismissCurrentAnimated(true) } } ================================================ FILE: Demo/Demo/FeedRequest.swift ================================================ // // FeedRequest.swift // Demo // // Created by zhuchao on 15/5/14. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS class FeedRequest: EZRequest { //如果不使用staticPath,可以设置如下参数,同OC版本的使用方法 // var feedId:String? // var type:String? override init() { super.init() self.staticPath = "http://7tsz2s.com1.z0.glb.clouddn.com/feedlists.txt" } } ================================================ FILE: Demo/Demo/FlexScene.swift ================================================ // // FlexScene.swift // Demo // // Created by zhuchao on 15/10/14. // Copyright © 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS class FlexScene: EZScene { var container:FLEXBOXContainerView! override func viewDidLoad() { super.viewDidLoad() container = FLEXBOXContainerView(frame: self.view.bounds) container.flexJustifyContent = FLEXBOXJustification.Center container.flexAlignItems = FLEXBOXAlignment.Center container.flexDirection = FLEXBOXFlexDirection.Column container.backgroundColor = UIColor.grayColor() let v1 = UILabel() v1.text = "123123" v1.backgroundColor = UIColor.blueColor() v1.textAlignment = NSTextAlignment.Center v1.flexMargin = UIEdgeInsetsMake(8, 8, 8, 8) v1.flexPadding = UIEdgeInsetsMake(10, 10, 10, 10) v1.flex = 0 container.addSubview(v1) let v2 = UILabel() v2.text = "321" v2.backgroundColor = UIColor.blueColor() v2.textAlignment = NSTextAlignment.Center v2.flexMargin = UIEdgeInsetsMake(8, 8, 8, 8) v2.flexPadding = UIEdgeInsetsMake(10, 10, 10, 10) v2.flex = 0 container.addSubview(v2) self.view.addSubview(container) } } ================================================ FILE: Demo/Demo/FlexXmlScene.swift ================================================ // // FlexXmlScene.swift // Demo // // Created by zhuchao on 15/10/14. // Copyright © 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS class FlexXmlScene: EUScene { override func viewDidLoad() { super.viewDidLoad() self.title = "FLEX BOX" // Do any additional setup after loading the view, typically from a nib. } } ================================================ FILE: Demo/Demo/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Demo/Demo/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Demo/Demo/LabelScene.swift ================================================ // // LabelScene.swift // Demo // // Created by zhuchao on 15/5/18. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import EasyIOS import TTTAttributedLabel class LabelScene: EUScene { override func viewDidLoad() { super.viewDidLoad() self.showBarButton(.LEFT, title: "返回", fontColor: UIColor.greenColor()) self.title = "界面里只有1个Label" if let label = getElementById("labelId") as? TTTAttributedLabel { //do someting } // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func eu_viewWillLoad() { self.attributedLabelDelegate = MainLabelDeleage() } override func leftButtonTouch() { URLNavigation.dismissCurrentAnimated(true) } } ================================================ FILE: Demo/Demo/LoginScene.swift ================================================ // // ViewController.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import EasyIOS class LoginScene: EUScene { override func viewDidLoad() { super.viewDidLoad() self.showBarButton(.LEFT, title: "返回", fontColor: UIColor.greenColor()) // define("login"){ // println("登陆回调") // } //在模拟器中调用下面这个方法可以生成一个加密文件 // EUI.encode("LoginScene", suffix: "xml", toPath: "/Users/zhuchao/Desktop/EncodeLoginScene.crypto") // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func leftButtonTouch() { URLNavigation.dismissCurrentAnimated(true) } } ================================================ FILE: Demo/Demo/MainLabelDeleage.swift ================================================ // // MainLabelDeleage.swift // Demo // // Created by zhuchao on 15/5/17. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import EasyIOS import TTTAttributedLabel class MainLabelDeleage: TTTAttributedLabelDelegateHandle { func attributedLabel(label: TTTAttributedLabel!, didSelectLinkWithURL url: NSURL!) { URLManager.pushURL(url, animated: true) } } ================================================ FILE: Demo/Demo/MainScene.swift ================================================ // // MainScene.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import Bond import EasyIOS class MainScene: EUScene,UITableViewDelegate{ var sceneModel = MainSceneModel() override func viewDidLoad() { super.viewDidLoad() self.title = "EasyIOS" // let label = UILabel() // label.flexAlignItems = FLEXBOXAlignment.Center // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } //do someThing init before loadTheView override func eu_viewWillLoad() { self.attributedLabelDelegate = MainLabelDeleage() } override func eu_tableViewDidLoad(tableView:UITableView?){ tableView?.delegate = self //定义一个可以给JS调用的下拉刷新回调方法handlePullRefresh() define("handlePullRefresh"){ let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC))) dispatch_after(delayTime, dispatch_get_main_queue()) { tableView?.pullToRefreshView?.stopAnimating() } } ObservableArray([ self.sceneModel.dataArray, self.sceneModel.dataArray, self.sceneModel.dataArray]) .bindTo(tableView!) { (indexPath, dataArray, tableView) -> UITableViewCell in let data = dataArray[indexPath.section][indexPath.row] let cell = tableView.dequeueReusableCell("cell", forIndexPath: indexPath, target: self, bind: data) cell.selectionStyle = .None return cell } } //xml里已经有了tap点击事件,这里就不调用了 // func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { // var model = self.sceneModel.dataArray[indexPath.row] // if let link = model.link { // URLManager.pushURLString(link, animated: true) // } // } func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 23.0 } func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { tableView return tableView.getSectionViewByTagId("bgView", target: self,bind:sceneModel.sectionArray[section]) } } ================================================ FILE: Demo/Demo/MainSceneModel.swift ================================================ // // MainSsceneModel.swift // Demo // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import Bond import EasyIOS class MainCellViewModel: EZViewModel { var title:String? var subTitle:NSData? var srcUrl:NSURL? var link:String? init(title:String,subTitle:String = "",srcUrl:String = "",link:String = ""){ self.title = title if let data = subTitle.toData() { self.subTitle = data } self.srcUrl = NSURL(string: srcUrl) self.link = link } } class MainSectionViewModel: EZViewModel { var title:String? init(title:String){ self.title = title } } class MainSceneModel: EZSceneModel { var dataArray = ObservableArray(Array()) var sectionArray = ObservableArray(Array()) override init (){ super.init() self.dataArray.append(MainCellViewModel( title: "一个简单的登陆页面", subTitle: "UIScrollView的使用Demo", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://login")) self.dataArray.append(MainCellViewModel( title: "还是登陆界面不过是加密版本", subTitle: "UIScrollView的使用Demo", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://encodelogin")) self.dataArray.append(MainCellViewModel( title: "Collection+网络请求+下拉控件", subTitle: "UICollectionView的使用Demo,包含了网络请求", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://collection")) self.dataArray.append(MainCellViewModel( title: "HTML版的Label,采用TTTAttributeLabel", subTitle: "

Hello World!

", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://LabelScene")) self.dataArray.append(MainCellViewModel( title: "native版本的flex模型演示", subTitle: "native版本的flex模型演示", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://flex")) self.dataArray.append(MainCellViewModel( title: "xml版本的flex模型演示", subTitle: "xml版本的flex模型演示", srcUrl: "http://d.hiphotos.baidu.com/zhidao/pic/item/562c11dfa9ec8a13e028c4c0f603918fa0ecc0e4.jpg", link:"demo://flexml")) self.sectionArray.append(MainSectionViewModel( title: "section 1")) self.sectionArray.append(MainSectionViewModel( title: "section 2")) self.sectionArray.append(MainSectionViewModel( title: "section 3")) } } ================================================ FILE: Demo/Demo/url.plist ================================================ demo demo://flexml FlexXmlScene demo://flex FlexScene demo://encodelogin EncodeLoginScene demo://collection CollectionScene demo://login LoginScene ================================================ FILE: Demo/Demo/xml.bundle/CollectionScene.xml ================================================
{{name}}
================================================ FILE: Demo/Demo/xml.bundle/EncodeLoginScene.crypto ================================================ VTWWEpHde9NHKxqWASruTqqmlAB2w6yrgF3k4/6aH5xLCWW09IBo0wCd/XDQTXFG H/fTwKRPrtiF85H9augC+XEcQ2BavK7sIbi99qwL0TaJOnmMoEt8n65i/BpOuCXe U/uYXAQTOc8L43XQMRQ5o6bd0928uEK8tm/khgYB7ho2nc/LnSyk387N+7fVWasI 7ygbDY1yzWsbLYXdBju9fKvku6rqyRFUTLR3Y0LyyhNaqcK3QWkPGf0C1SpEvZN5 f/XX8+YL0sf/bMngbrOaNKQtZVQJfxJUSP4BUOHXql9Bca7o1hGERQ2JJRhBtfis o/YWFO58QO16gHRrkqmXzHndkOknrBB0lalewwMathppGCUztFS3+D4c4Bxb8kzD eBHwU/e5V/NWTKbje0EpX2aTLl1jHarGLqsybsEb2xACBoumJrdcATPdnXl/Ouou NiKoS2h8muGLO46oFyusalX+/1o7wIQDG3rwhwsMKpZvTatpaAhRJlvY5/yHUDUN iLTu0JO73PctuRI8nTpx6D9+nlw2zo/OmsuvvxldLmv9FhAs5XvcJnrnMr4z6S8o 4ohBH79i9UJfDaYxAaofMrI5szIYu3DG4qCM8NyNFQc7YoeKN1wQjat41Wenxx5B RrtMke5dKlE0ATstakRmdNa4zqzPFkhxtVPuK4gVn7n5EPUDaz/flC3VA4zxxD7k uo0FfD4uEREvM9UcKIXuaV+8Riu26ly2khMbPznGkKxTVeu2SWecu/HHQRPANz5S ueyPZvELpAbiR7usH4IY37IUWz2vteVOYmwHPV+09E74YfRsoHtOEO8r4YAi+OyO ZsXa2wn5+TSUFODny6k7y7XWpp4XIcQBDW+08lg+eLuXfoEUttw/JzqSgxxcs/F+ R9tbVIW0MAVXThjVcaD3IHnvRYeZsdj3JUDpeGOv9WbIqcYz0Fgwkg7Lav4QOy+U SNPXnuCDw7OhsCfbifmxTCTuDPZ6HjGtFauiGEN83EpJzQGAYEktvyyEXhhS8LKk eL/JRag25fR0VLQPCNk/5e2SP/dOI7ReR5y4l9NR5oFteKDuQytUrDiHm9f6Bn+T MMuK4jhQ3QpmuaT1Ps8jSW3hnK6zUXStFmOgRzmDTQnGqkzHybo9W9Ewj9nWINEO PIz/wPpVO4ok7PuxP+sLo6xdUCY6oZFhRCU4k03SQhyk6jari2PxHD4DuiyjcLLU IW+dFxscF5dhPi0TTdSZfrD6T53NsHeLDXBECmabKT+mugc2IsV+GG9N3rhwXhyO 4mduo2v0XsNLaYAqJdZue6zcw/o40mWoPCpK37ZfSeaABEey5UUVlc+fJR6+ct20 wRlvazYrOqWKW8C5I/ZXD/ei0v7hB+KB87Q40RBEJ27uDH8mITgWqg/pwMqBAs30 7+5lrddB79wuRRHIbE907uad8seRXT8CA0QBpSER9aXfet5SK6YkhP6Ve/p7OIt5 Ym4ZhtR377+YV3dLh08xNlFJ4Fd8iE/NSvRFoVFUb0s5DPQaUL42Sd2hH3WFz3U4 W2dMZ4Cnj89i56XNnA+Gwe+eaqrQmHpLVIeAfTlnmXjUWaOFHj6Xg91drLPPyFTH 64olhQImSa6xGN3wVdDDUEN6Oz3D2YBI5gV7YQVQkMZyqV7fgjUwY7uDCStgvHDc b64/PrtWlDCqGN79ncTKomuHhUszIhnrRxspp4u5dPim2nd45/34LEvVMF7+exZ5 RUXRKIlg4wXznUhmuAwjCuRgH88dk3UWbIfYFt52sOC1rTm5mMNcF15jRgvxZHua 89/jcEJoTfelbMTqgoJoNQ7ZiOldxHyseqxjVHaZg/yVUgWaK+yl9n/8vXC+eGsn Bm86QHd3JCwlaKoW8hvo2R7ntnLlyp6to+gzq//o3h/wzoQJehkubFyYug7cq1xd 7pMf/OFSiTulbDK8NzGqU+JN1EHalRgoNLZMKsrHgwlW0CVDDq1TO2WLjrk+cZXB jBbiQqW35gUK81vkqFm01BFUd/vWWG1X+cGfRgLPcMNYNHUdkYhR2MXE13o+yVUf chDkfRgiif68lYAoP74njGQcXwAugTB9bkQIYyQCuMIxaT1W+rgieCX4ieqaa+hw HmSd9D96UVPbhhISqZINkbmzoKsEA+iD4qqWl9/L7Kt9QESiTnRZvQW9Pi3hkEcU d79thZ8DwCkS/1KeFJonFxe59adBZyL5d6ttXDoTBNfGrfVNEntzkj/fmTWpNYpx LuDrc92OEylJs7Z0Utz3XX9dGU24qBCfQeEVoAK78eggtBwFxRgJmZ9f8acBI5tP ezLDkl9eioV0hoSI1Pgfdgc38QzNJJIG98zh4mPeOmOB/aumVW1iT/cZpEPK09XH n7vAcQxhQL6ksZdKXSqEXcDQPgrUro19qqW6fwCZcNvuvZQOA9PKmg== ================================================ FILE: Demo/Demo/xml.bundle/FlexXmlScene.xml ================================================
3 2 1
3 2 1
1 1 1 1 1
================================================ FILE: Demo/Demo/xml.bundle/LabelHtml.xml ================================================

EasyIOS For Swift

Star is the Best Way to Support EasyIOS !

Features

  • MVVM : Model-View-ViewModel inspired by Functional Reactive Programming
  • HTML To Native : Transform HTML&CSS to Native Control,
  • Reflect Cocoa Touch : Reflect all the Cocoa Touch Api ,we can use the Cocoa Touch Api via HTML
  • AutoLayout : The HTML layout based on the AutoLayout
  • Live Load : Edit the HTML and the view in smulator will update automaticly without rebuild your app
  • Cryptographic HTML : To make the HTML be safety,we provide the AES Encryption to encrypt the HTML
  • URLManager : Push or Present the Controller by the custom URL
  • Elegant PullToRefresh : Add PullToRefresh or InfiniteScrolling by HTML

Usage

To run the example project, clone the repo, and run pod install from the Demo directory first.

Requirements

  • Swift
  • IOS8

Author

zhuchao, zhuchao@iosx.me

License

EasyIOS-Swift is available under the MIT license. See the LICENSE file for more info.

================================================ FILE: Demo/Demo/xml.bundle/LabelScene.xml ================================================
@import(LabelHtml)
================================================ FILE: Demo/Demo/xml.bundle/LoginScene.xml ================================================
================================================ FILE: Demo/Demo/xml.bundle/MainScene.xml ================================================
{{title}} {{subTitle}}
{{title}}
================================================ FILE: Demo/Demo.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 5B3FCA9A1BCDF6AB00AF20CD /* FlexScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3FCA991BCDF6AB00AF20CD /* FlexScene.swift */; settings = {ASSET_TAGS = (); }; }; 5B4C52211BCE2E91003E0016 /* FlexXmlScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4C52201BCE2E91003E0016 /* FlexXmlScene.swift */; settings = {ASSET_TAGS = (); }; }; 5B4C52291BCE547A003E0016 /* Chinese.md in Sources */ = {isa = PBXBuildFile; fileRef = 5B4C52271BCE547A003E0016 /* Chinese.md */; settings = {ASSET_TAGS = (); }; }; 5B4C522A1BCE547A003E0016 /* flexbox.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4C52281BCE547A003E0016 /* flexbox.png */; settings = {ASSET_TAGS = (); }; }; 5B6512B11B141A9E00B1BA89 /* EncodeLoginScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6512B01B141A9E00B1BA89 /* EncodeLoginScene.swift */; }; 5B7B80C51B08D8E00059CAEF /* MainLabelDeleage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7B80C41B08D8E00059CAEF /* MainLabelDeleage.swift */; }; 5B7B80DB1B0A0B2A0059CAEF /* LabelScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7B80DA1B0A0B2A0059CAEF /* LabelScene.swift */; }; 5B8240C01B03715600D58574 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8240BF1B03715600D58574 /* AppDelegate.swift */; }; 5B8240C21B03715600D58574 /* LoginScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8240C11B03715600D58574 /* LoginScene.swift */; }; 5B8240C71B03715600D58574 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B8240C61B03715600D58574 /* Images.xcassets */; }; 5B8240CA1B03715600D58574 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B8240C81B03715600D58574 /* LaunchScreen.xib */; }; 5B8240D61B03715600D58574 /* DemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8240D51B03715600D58574 /* DemoTests.swift */; }; 5B8240E91B0377FC00D58574 /* EasyIOS-Swift.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 5B8240E61B0377FC00D58574 /* EasyIOS-Swift.podspec */; }; 5B8240EA1B0377FC00D58574 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 5B8240E71B0377FC00D58574 /* LICENSE */; }; 5B8240EB1B0377FC00D58574 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 5B8240E81B0377FC00D58574 /* README.md */; }; 5B90B5CD1B04BFF8000C848D /* FeedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B90B5CC1B04BFF8000C848D /* FeedRequest.swift */; }; 5BBC33DA1BB3DFE00030B054 /* xml.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5BBC33D91BB3DFE00030B054 /* xml.bundle */; settings = {ASSET_TAGS = (); }; }; 5BDBF0CE1B038B7300E95ACF /* login-logo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BDBF0CD1B038B7300E95ACF /* login-logo@2x.png */; }; 5BDBF0D01B038C1700E95ACF /* MainScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDBF0CF1B038C1700E95ACF /* MainScene.swift */; }; 5BDBF0D51B038C7800E95ACF /* MainSceneModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDBF0D41B038C7800E95ACF /* MainSceneModel.swift */; }; 5BDBF0D91B038F0D00E95ACF /* url.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BDBF0D81B038F0D00E95ACF /* url.plist */; }; 5BDC06BB1B0394C200C486E0 /* CollectionScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDC06BA1B0394C200C486E0 /* CollectionScene.swift */; }; 5BDC06BD1B03950500C486E0 /* CollectionSceneModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDC06BC1B03950500C486E0 /* CollectionSceneModel.swift */; }; 5BDC06BF1B03955A00C486E0 /* CollectionCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDC06BE1B03955A00C486E0 /* CollectionCellViewModel.swift */; }; 87D67298D115B9D5014DB706 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CB89BDD03972BB5036E6DF /* Pods.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 5B8240D01B03715600D58574 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5B8240B21B03715600D58574 /* Project object */; proxyType = 1; remoteGlobalIDString = 5B8240B91B03715600D58574; remoteInfo = Demo; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 0BAFEE4C5E077414790C2042 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 55CB89BDD03972BB5036E6DF /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5B3FCA991BCDF6AB00AF20CD /* FlexScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexScene.swift; sourceTree = ""; }; 5B4C52201BCE2E91003E0016 /* FlexXmlScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexXmlScene.swift; sourceTree = ""; }; 5B4C52271BCE547A003E0016 /* Chinese.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Chinese.md; sourceTree = ""; }; 5B4C52281BCE547A003E0016 /* flexbox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = flexbox.png; sourceTree = ""; }; 5B6512B01B141A9E00B1BA89 /* EncodeLoginScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodeLoginScene.swift; sourceTree = ""; }; 5B7B80C41B08D8E00059CAEF /* MainLabelDeleage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainLabelDeleage.swift; sourceTree = ""; }; 5B7B80DA1B0A0B2A0059CAEF /* LabelScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelScene.swift; sourceTree = ""; }; 5B8240BA1B03715600D58574 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5B8240BE1B03715600D58574 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5B8240BF1B03715600D58574 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 5B8240C11B03715600D58574 /* LoginScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScene.swift; sourceTree = ""; }; 5B8240C61B03715600D58574 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 5B8240C91B03715600D58574 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 5B8240CF1B03715600D58574 /* DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5B8240D41B03715600D58574 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5B8240D51B03715600D58574 /* DemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoTests.swift; sourceTree = ""; }; 5B8240E61B0377FC00D58574 /* EasyIOS-Swift.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "EasyIOS-Swift.podspec"; path = "../EasyIOS-Swift.podspec"; sourceTree = ""; }; 5B8240E71B0377FC00D58574 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 5B8240E81B0377FC00D58574 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 5B90B5CC1B04BFF8000C848D /* FeedRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedRequest.swift; sourceTree = ""; }; 5BBC33D91BB3DFE00030B054 /* xml.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = xml.bundle; sourceTree = ""; }; 5BDBF0CD1B038B7300E95ACF /* login-logo@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "login-logo@2x.png"; sourceTree = ""; }; 5BDBF0CF1B038C1700E95ACF /* MainScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainScene.swift; sourceTree = ""; }; 5BDBF0D41B038C7800E95ACF /* MainSceneModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainSceneModel.swift; sourceTree = ""; }; 5BDBF0D81B038F0D00E95ACF /* url.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = url.plist; sourceTree = ""; }; 5BDC06BA1B0394C200C486E0 /* CollectionScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionScene.swift; sourceTree = ""; }; 5BDC06BC1B03950500C486E0 /* CollectionSceneModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionSceneModel.swift; sourceTree = ""; }; 5BDC06BE1B03955A00C486E0 /* CollectionCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionCellViewModel.swift; sourceTree = ""; }; 6FA5AECAF1463C83BB66DE59 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 5B8240B71B03715600D58574 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 87D67298D115B9D5014DB706 /* Pods.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 5B8240CC1B03715600D58574 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1729A45EA55A2C898A1E3A6F /* Pods */ = { isa = PBXGroup; children = ( 6FA5AECAF1463C83BB66DE59 /* Pods.debug.xcconfig */, 0BAFEE4C5E077414790C2042 /* Pods.release.xcconfig */, ); name = Pods; sourceTree = ""; }; 5B3FCA981BCDF68B00AF20CD /* FlexScene */ = { isa = PBXGroup; children = ( 5B3FCA991BCDF6AB00AF20CD /* FlexScene.swift */, 5B4C52201BCE2E91003E0016 /* FlexXmlScene.swift */, ); name = FlexScene; sourceTree = ""; }; 5B4C52261BCE547A003E0016 /* README */ = { isa = PBXGroup; children = ( 5B4C52271BCE547A003E0016 /* Chinese.md */, 5B4C52281BCE547A003E0016 /* flexbox.png */, ); name = README; path = ../README; sourceTree = ""; }; 5B7B80D91B0A0B0F0059CAEF /* LabelScene */ = { isa = PBXGroup; children = ( 5B7B80DA1B0A0B2A0059CAEF /* LabelScene.swift */, ); name = LabelScene; sourceTree = ""; }; 5B8240B11B03715600D58574 = { isa = PBXGroup; children = ( 5B8240DF1B0372B500D58574 /* PodMeta */, 5B8240BC1B03715600D58574 /* Demo */, 5B8240D21B03715600D58574 /* DemoTests */, 5B8240BB1B03715600D58574 /* Products */, 1729A45EA55A2C898A1E3A6F /* Pods */, 82578B0764B5D4425C7B4BC5 /* Frameworks */, ); sourceTree = ""; }; 5B8240BB1B03715600D58574 /* Products */ = { isa = PBXGroup; children = ( 5B8240BA1B03715600D58574 /* Demo.app */, 5B8240CF1B03715600D58574 /* DemoTests.xctest */, ); name = Products; sourceTree = ""; }; 5B8240BC1B03715600D58574 /* Demo */ = { isa = PBXGroup; children = ( 5B3FCA981BCDF68B00AF20CD /* FlexScene */, 5B7B80D91B0A0B0F0059CAEF /* LabelScene */, 5B90B5C11B043889000C848D /* LoginScene */, 5BDC06B91B03949900C486E0 /* CollectionScene */, 5BDBF0D31B038C6400E95ACF /* MainScene */, 5BDBF0CC1B038B6800E95ACF /* Rescource */, 5B8240BF1B03715600D58574 /* AppDelegate.swift */, 5B8240C61B03715600D58574 /* Images.xcassets */, 5B8240C81B03715600D58574 /* LaunchScreen.xib */, 5B8240BD1B03715600D58574 /* Supporting Files */, 5BBC33D91BB3DFE00030B054 /* xml.bundle */, ); path = Demo; sourceTree = ""; }; 5B8240BD1B03715600D58574 /* Supporting Files */ = { isa = PBXGroup; children = ( 5B8240BE1B03715600D58574 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 5B8240D21B03715600D58574 /* DemoTests */ = { isa = PBXGroup; children = ( 5B8240D51B03715600D58574 /* DemoTests.swift */, 5B8240D31B03715600D58574 /* Supporting Files */, ); path = DemoTests; sourceTree = ""; }; 5B8240D31B03715600D58574 /* Supporting Files */ = { isa = PBXGroup; children = ( 5B8240D41B03715600D58574 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 5B8240DF1B0372B500D58574 /* PodMeta */ = { isa = PBXGroup; children = ( 5B4C52261BCE547A003E0016 /* README */, 5B8240E61B0377FC00D58574 /* EasyIOS-Swift.podspec */, 5B8240E71B0377FC00D58574 /* LICENSE */, 5B8240E81B0377FC00D58574 /* README.md */, ); name = PodMeta; sourceTree = ""; }; 5B90B5C11B043889000C848D /* LoginScene */ = { isa = PBXGroup; children = ( 5B8240C11B03715600D58574 /* LoginScene.swift */, 5B6512B01B141A9E00B1BA89 /* EncodeLoginScene.swift */, ); name = LoginScene; sourceTree = ""; }; 5BDBF0CC1B038B6800E95ACF /* Rescource */ = { isa = PBXGroup; children = ( 5BDBF0D81B038F0D00E95ACF /* url.plist */, 5BDBF0CD1B038B7300E95ACF /* login-logo@2x.png */, ); name = Rescource; sourceTree = ""; }; 5BDBF0D31B038C6400E95ACF /* MainScene */ = { isa = PBXGroup; children = ( 5BDBF0CF1B038C1700E95ACF /* MainScene.swift */, 5BDBF0D41B038C7800E95ACF /* MainSceneModel.swift */, 5B7B80C41B08D8E00059CAEF /* MainLabelDeleage.swift */, ); name = MainScene; sourceTree = ""; }; 5BDC06B91B03949900C486E0 /* CollectionScene */ = { isa = PBXGroup; children = ( 5BDC06BA1B0394C200C486E0 /* CollectionScene.swift */, 5BDC06BC1B03950500C486E0 /* CollectionSceneModel.swift */, 5BDC06BE1B03955A00C486E0 /* CollectionCellViewModel.swift */, 5B90B5CC1B04BFF8000C848D /* FeedRequest.swift */, ); name = CollectionScene; sourceTree = ""; }; 82578B0764B5D4425C7B4BC5 /* Frameworks */ = { isa = PBXGroup; children = ( 55CB89BDD03972BB5036E6DF /* Pods.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 5B8240B91B03715600D58574 /* Demo */ = { isa = PBXNativeTarget; buildConfigurationList = 5B8240D91B03715600D58574 /* Build configuration list for PBXNativeTarget "Demo" */; buildPhases = ( 58FFA88B0BBAEE141CD3F935 /* Check Pods Manifest.lock */, 5B8240B61B03715600D58574 /* Sources */, 5B8240B71B03715600D58574 /* Frameworks */, 5B8240B81B03715600D58574 /* Resources */, B9036220CE63B1B76499B1DF /* Embed Pods Frameworks */, B0EA13793EF213CED106EF77 /* Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Demo; productName = Demo; productReference = 5B8240BA1B03715600D58574 /* Demo.app */; productType = "com.apple.product-type.application"; }; 5B8240CE1B03715600D58574 /* DemoTests */ = { isa = PBXNativeTarget; buildConfigurationList = 5B8240DC1B03715600D58574 /* Build configuration list for PBXNativeTarget "DemoTests" */; buildPhases = ( 5B8240CB1B03715600D58574 /* Sources */, 5B8240CC1B03715600D58574 /* Frameworks */, 5B8240CD1B03715600D58574 /* Resources */, ); buildRules = ( ); dependencies = ( 5B8240D11B03715600D58574 /* PBXTargetDependency */, ); name = DemoTests; productName = DemoTests; productReference = 5B8240CF1B03715600D58574 /* DemoTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 5B8240B21B03715600D58574 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0700; ORGANIZATIONNAME = zhuchao; TargetAttributes = { 5B8240B91B03715600D58574 = { CreatedOnToolsVersion = 6.3.1; }; 5B8240CE1B03715600D58574 = { CreatedOnToolsVersion = 6.3.1; TestTargetID = 5B8240B91B03715600D58574; }; }; }; buildConfigurationList = 5B8240B51B03715600D58574 /* Build configuration list for PBXProject "Demo" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 5B8240B11B03715600D58574; productRefGroup = 5B8240BB1B03715600D58574 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 5B8240B91B03715600D58574 /* Demo */, 5B8240CE1B03715600D58574 /* DemoTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 5B8240B81B03715600D58574 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 5BBC33DA1BB3DFE00030B054 /* xml.bundle in Resources */, 5B8240CA1B03715600D58574 /* LaunchScreen.xib in Resources */, 5B8240E91B0377FC00D58574 /* EasyIOS-Swift.podspec in Resources */, 5BDBF0CE1B038B7300E95ACF /* login-logo@2x.png in Resources */, 5B8240C71B03715600D58574 /* Images.xcassets in Resources */, 5BDBF0D91B038F0D00E95ACF /* url.plist in Resources */, 5B4C522A1BCE547A003E0016 /* flexbox.png in Resources */, 5B8240EA1B0377FC00D58574 /* LICENSE in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 5B8240CD1B03715600D58574 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 58FFA88B0BBAEE141CD3F935 /* Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; B0EA13793EF213CED106EF77 /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; B9036220CE63B1B76499B1DF /* Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 5B8240B61B03715600D58574 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 5BDC06BD1B03950500C486E0 /* CollectionSceneModel.swift in Sources */, 5B90B5CD1B04BFF8000C848D /* FeedRequest.swift in Sources */, 5B6512B11B141A9E00B1BA89 /* EncodeLoginScene.swift in Sources */, 5B4C52291BCE547A003E0016 /* Chinese.md in Sources */, 5B4C52211BCE2E91003E0016 /* FlexXmlScene.swift in Sources */, 5BDBF0D51B038C7800E95ACF /* MainSceneModel.swift in Sources */, 5BDC06BF1B03955A00C486E0 /* CollectionCellViewModel.swift in Sources */, 5B3FCA9A1BCDF6AB00AF20CD /* FlexScene.swift in Sources */, 5BDC06BB1B0394C200C486E0 /* CollectionScene.swift in Sources */, 5B7B80DB1B0A0B2A0059CAEF /* LabelScene.swift in Sources */, 5B8240EB1B0377FC00D58574 /* README.md in Sources */, 5BDBF0D01B038C1700E95ACF /* MainScene.swift in Sources */, 5B8240C21B03715600D58574 /* LoginScene.swift in Sources */, 5B8240C01B03715600D58574 /* AppDelegate.swift in Sources */, 5B7B80C51B08D8E00059CAEF /* MainLabelDeleage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 5B8240CB1B03715600D58574 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 5B8240D61B03715600D58574 /* DemoTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 5B8240D11B03715600D58574 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 5B8240B91B03715600D58574 /* Demo */; targetProxy = 5B8240D01B03715600D58574 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 5B8240C81B03715600D58574 /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 5B8240C91B03715600D58574 /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 5B8240D71B03715600D58574 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.3; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 5B8240D81B03715600D58574 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 5B8240DA1B03715600D58574 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 6FA5AECAF1463C83BB66DE59 /* Pods.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = Demo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.iosx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_INSTALL_OBJC_HEADER = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 5B8240DB1B03715600D58574 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 0BAFEE4C5E077414790C2042 /* Pods.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = Demo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.iosx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_INSTALL_OBJC_HEADER = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; }; name = Release; }; 5B8240DD1B03715600D58574 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = DemoTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.iosx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; }; name = Debug; }; 5B8240DE1B03715600D58574 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); INFOPLIST_FILE = DemoTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.iosx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 5B8240B51B03715600D58574 /* Build configuration list for PBXProject "Demo" */ = { isa = XCConfigurationList; buildConfigurations = ( 5B8240D71B03715600D58574 /* Debug */, 5B8240D81B03715600D58574 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 5B8240D91B03715600D58574 /* Build configuration list for PBXNativeTarget "Demo" */ = { isa = XCConfigurationList; buildConfigurations = ( 5B8240DA1B03715600D58574 /* Debug */, 5B8240DB1B03715600D58574 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 5B8240DC1B03715600D58574 /* Build configuration list for PBXNativeTarget "DemoTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 5B8240DD1B03715600D58574 /* Debug */, 5B8240DE1B03715600D58574 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 5B8240B21B03715600D58574 /* Project object */; } ================================================ FILE: Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Demo/Demo.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Demo/DemoTests/DemoTests.swift ================================================ // // DemoTests.swift // DemoTests // // Created by zhuchao on 15/5/13. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import XCTest class DemoTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. self.measureBlock() { // Put the code you want to measure the time of here. } } } ================================================ FILE: Demo/DemoTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: Demo/Podfile ================================================ platform :ios, '8.0' use_frameworks! pod "EasyIOS-Swift", :path => "../" pod 'SVProgressHUD', '~> 1.1.3' ================================================ FILE: EasyIOS-Swift.podspec ================================================ # # Be sure to run `pod lib lint EasyIOS-Swift.podspec' to ensure this is a # valid spec and remove all comments before submitting the spec. # # Any lines starting with a # are optional, but encouraged # # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| s.name = "EasyIOS-Swift" s.version = "2.0.1" s.summary = "The Swift version of EasyIOS" s.description = <<-DESC EasyIOS is a new generation of development framework based on `Model-View-ViewModel`,`HTML To Native`,`Live Load`,`FlexBox`. DESC s.homepage = "https://github.com/EasyIOS/EasyIOS-Swift" # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" s.license = 'MIT' s.author = { "zhuchao" => "zhuchao@iosx.me" } s.source = { :git => "https://github.com/EasyIOS/EasyIOS-Swift.git", :tag => s.version.to_s } #s.source = { :git => "/Users/zhuchao/Documents/EasyIOS-Swift"} s.social_media_url = 'https://twitter.com/zhuchaowe' s.platform = :ios, '8.0' s.requires_arc = true s.module_name = "EasyIOS" s.source_files = 'Pod/Classes/**/*' s.resource_bundles = {'EasyIOS-Swift' => ['Pod/Assets/*.png']} s.dependency 'HanekeSwift' s.dependency 'Bond' s.dependency 'Alamofire' s.dependency 'SnapKit' s.dependency 'Kingfisher' s.dependency 'ObjectMapper' s.dependency 'ReachabilitySwift' s.dependency 'TTTAttributedLabel' s.public_header_files = 'Pod/Classes/Easy/**/*.h','Pod/Classes/Extend/**/*.h','Pod/Classes/Private/**/*.h' s.frameworks = 'UIKit','JavaScriptCore' end ================================================ FILE: LICENSE ================================================ Copyright (c) 2015 zhuchao Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Pod/Assets/.gitkeep ================================================ ================================================ FILE: Pod/Classes/.gitkeep ================================================ ================================================ FILE: Pod/Classes/Easy/Core/EZAction.swift ================================================ // // Action.swift // medical // // Created by zhuchao on 15/4/25. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Alamofire import Haneke import Bond import ReachabilitySwift public var HOST_URL = "" //服务端域名:端口 public var CODE_KEY = "" //错误码key,暂不支持路径 如 code public var RIGHT_CODE = 0 //正确校验码 public var MSG_KEY = "" //消息提示msg,暂不支持路径 如 msg private var networkReachabilityHandle: UInt8 = 2; public class EZAction: NSObject { //使用缓存策略 仅首次读取缓存 public class func SEND_IQ_CACHE (req:EZRequest) { req.useCache = true req.dataFromCache = req.isFirstRequest self.Send(req) } //使用缓存策略 优先从缓存读取 public class func SEND_CACHE (req:EZRequest) { req.useCache = true req.dataFromCache = true self.Send(req) } //不使用缓存策略 public class func SEND (req:EZRequest) { req.useCache = false req.dataFromCache = false self.Send(req) } public class func Send (req :EZRequest){ var url = "" var requestParams = Dictionary() if !req.staticPath.characters.isEmpty { url = req.staticPath }else{ if req.scheme.characters.isEmpty { req.scheme = "http" } if req.host.characters.isEmpty { req.host = HOST_URL } url = req.scheme + "://" + req.host + req.path if req.appendPathInfo.characters.isEmpty { requestParams = req.requestParams }else{ url = url + req.appendPathInfo } } req.state.value = RequestState.Sending req.op = req.manager .request(req.method, url, parameters: requestParams, encoding: req.parameterEncoding) .validate(statusCode: 200..<300) .validate(contentType: req.acceptableContentTypes) .responseJSON { response in req.response = response if response.result.isFailure{ req.error = response.result.error self.failed(req) }else{ req.output = response.result.value as! Dictionary self.checkCode(req) } } req.url = req.op?.request!.URL self.getCacheJson(req) } public class func Upload (req :EZRequest){ req.state.value = RequestState.Sending req.op = req.manager .upload(.POST, req.uploadUrl, data: req.uploadData!) .validate(statusCode: 200..<300) .validate(contentType: req.acceptableContentTypes) .progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in req.totalBytesWritten = Double(totalBytesWritten) req.totalBytesExpectedToWrite = Double(totalBytesExpectedToWrite) req.progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) }.responseJSON { response in req.response = response if response.result.isFailure{ req.error = response.result.error self.failed(req) }else{ req.output = response.result.value as! Dictionary self.checkCode(req) } } req.url = req.op?.request!.URL } public class func Download (req :EZRequest){ req.state.value = RequestState.Sending req.op = req.manager .download(.GET, req.downloadUrl, destination: { (temporaryURL, response) in let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] return directoryURL.URLByAppendingPathComponent(req.targetPath + response.suggestedFilename!) }) .validate(statusCode: 200..<300) .progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) in req.totalBytesRead = Double(totalBytesRead) req.totalBytesExpectedToRead = Double(totalBytesExpectedToRead) req.progress = Double(totalBytesRead) / Double(totalBytesExpectedToRead) } .response { (request, response, _, error) in if error != nil{ req.error = error self.failed(req) }else{ req.state.value = RequestState.Success } } req.url = req.op?.request!.URL } private class func cacheJson (req: EZRequest) { if req.useCache { let cache = Shared.JSONCache cache.set(value: .Dictionary(req.output), key: req.cacheKey, formatName: HanekeGlobals.Cache.OriginalFormatName){ JSON in EZPrintln("Cache Success for key: \(req.cacheKey)") } } } private class func getCacheJson (req: EZRequest) { let cache = Shared.JSONCache cache.fetch(key: req.cacheKey).onSuccess { JSON in req.output = JSON.dictionary if req.dataFromCache && !isEmpty(req.output) { let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) dispatch_after(delayTime, dispatch_get_main_queue()) { self.loadFromCache(req) } } } } private class func loadFromCache (req: EZRequest){ if req.needCheckCode && req.state.value != .Success { req.codeKey = req.output[CODE_KEY] as? Int if req.codeKey == RIGHT_CODE { req.message = req.output[MSG_KEY] as? String req.state.value = RequestState.SuccessFromCache EZPrintln("Fetch Success from Cache by key: \(req.cacheKey)") }else{ req.message = req.output[MSG_KEY] as? String req.state.value = RequestState.ErrorFromCache EZPrintln(req.message) } } } private class func checkCode (req: EZRequest) { if req.needCheckCode { req.codeKey = req.output[CODE_KEY] as? Int if req.codeKey == RIGHT_CODE { self.success(req) self.cacheJson(req) }else{ self.error(req) } }else{ req.state.value = RequestState.Success self.cacheJson(req) } } private class func success (req: EZRequest) { req.isFirstRequest = false req.message = req.output[MSG_KEY] as? String if req.output.isEmpty { req.state.value = RequestState.Error }else{ req.state.value = RequestState.Success } } private class func failed (req: EZRequest) { req.message = req.error.debugDescription req.state.value = RequestState.Failed EZPrintln(req.message) } private class func error (req: EZRequest) { req.message = req.output[MSG_KEY] as? String req.state.value = RequestState.Error EZPrintln(req.message) } /* Usage EZAction.networkReachability *->> Bond{ status in switch (status) { case .NotReachable: EZPrintln("NotReachable") case .ReachableViaWiFi: EZPrintln("ReachableViaWiFi") case .ReachableViaWWAN: EZPrintln("ReachableViaWWAN") default: EZPrintln("default") } } */ public class var networkReachability: Observable? { if let d: AnyObject = objc_getAssociatedObject(self, &networkReachabilityHandle) { return d as? Observable } else { do { let reachability = try Reachability.reachabilityForInternetConnection() let d = Observable(reachability.currentReachabilityStatus) reachability.whenReachable = { reachability in dispatch_async(dispatch_get_main_queue()) { d.value = reachability.currentReachabilityStatus } } reachability.whenUnreachable = { reachability in dispatch_async(dispatch_get_main_queue()) { d.value = reachability.currentReachabilityStatus } } try reachability.startNotifier() objc_setAssociatedObject(self, &networkReachabilityHandle, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } catch { print("Unable to create Reachability") return nil } } } } ================================================ FILE: Pod/Classes/Easy/Core/EZCollectionView.swift ================================================ // // EZCollectionView.swift // medical // // Created by zhuchao on 15/4/26. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit public class EZCollectionView: UICollectionView { /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func drawRect(rect: CGRect) { // Drawing code } */ } ================================================ FILE: Pod/Classes/Easy/Core/EZNavigationController.swift ================================================ // // EZNavigationController.swift // medical // // Created by zhuchao on 15/4/24. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit public class EZNavigationController: UINavigationController,UINavigationControllerDelegate,UIGestureRecognizerDelegate{ public var popGestureRecognizerEnabled = true override public func viewDidLoad() { super.viewDidLoad() self.configGestureRecognizer() // Do any additional setup after loading the view. } override public func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } public func configGestureRecognizer() { if let target = self.interactivePopGestureRecognizer?.delegate { let pan = UIPanGestureRecognizer(target: target, action: Selector("handleNavigationTransition:")) pan.delegate = self self.view.addGestureRecognizer(pan) } //禁掉系统的侧滑手势 weak var weekSelf = self self.interactivePopGestureRecognizer?.enabled = false; self.interactivePopGestureRecognizer?.delegate = weekSelf; } public func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer != self.interactivePopGestureRecognizer && self.viewControllers.count > 1 && self.popGestureRecognizerEnabled{ return true }else{ return false } } override public func pushViewController(viewController: UIViewController, animated: Bool) { self.interactivePopGestureRecognizer?.enabled = false super.pushViewController(viewController, animated: animated) } //UINavigationControllerDelegate public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) { if self.popGestureRecognizerEnabled { self.interactivePopGestureRecognizer?.enabled = true } } } ================================================ FILE: Pod/Classes/Easy/Core/EZRequest.swift ================================================ // // Request.swift // medical // // Created by zhuchao on 15/4/24. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond import Alamofire public enum RequestState : Int { case Default //初始化状态 case Success case Failed case Sending case Error case Cancle case Suspend case SuccessFromCache case ErrorFromCache } private var enabledDynamicHandleRequest: UInt8 = 0 private var stateDynamicHandleRequest: UInt8 = 1 private var managerHandle: UInt8 = 2 public class EZRequest: NSObject { public var output = Dictionary() // 序列化后的数据 public var params = Dictionary() //使用字典参数 public var response:Response? // 获取字符串数据 public var error:ErrorType? //请求的错误 public var state = Observable(.Default) //Request状态 public var url:NSURL? //请求的链接 public var message:String? //错误消息或者服务器返回的MSG public var codeKey:Int? // 错误码返回 //upload上传相关参数 public var uploadUrl:URLStringConvertible = "" //上传图片到这个URL协议 public var uploadData:NSData? //上传文件 public var progress = 0.0 //上传进度 public var totalBytesWritten = 0.0 //已上传数据大小 public var totalBytesExpectedToWrite = 0.0 //全部需要上传的数据大小 // download下载相关参数 public var downloadUrl:URLStringConvertible = ""//下载图片URL public var targetPath = "" //下载到路径 public var totalBytesRead = 0.0 //已下载传数据大小 public var totalBytesExpectedToRead = 0.0 //全部需要下载的数据大小 public var scheme = "http" //协议 public var host = "" //域名 public var path = "" //请求路径 public var staticPath = "" //其他路径 public var method = Method.GET //提交方式 public var parameterEncoding = ParameterEncoding.URL //编码方式 Http头参数设置 public var needCheckCode = true //是否需要检查错误码 public var acceptableContentTypes = ["application/json","text/plain"] //可接受的序列化返回数据的格式 public var requestBlock:(Void->())? public var isFirstRequest = false //HttpHeader timeoutInterval Cookies 等都在这里设置 public var sessionConfiguration:NSURLSessionConfiguration? public var op:Request? public var requestNeedActive: Observable { if let d: AnyObject = objc_getAssociatedObject(self, &enabledDynamicHandleRequest) { return (d as? Observable)! } else { let d = Observable(false) d.observe{ [weak self] v in if let s = self { if v { d.value = false s.requestBlock?() } }} objc_setAssociatedObject(self, &enabledDynamicHandleRequest, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } } var useCache = false var dataFromCache = false public var requestKey :String { return self.nameOfClass } public class var requestKey:String { return self.nameOfClass } public var requestParams :Dictionary{ return self.listProperties() } public var appendPathInfo :String { var pathInfo = self.pathInfo if pathInfo != nil && !(pathInfo!).characters.isEmpty { for (key,nsValue) in self.requestParams { let par = "(\\{\(key)\\})" let str = "\(nsValue)" pathInfo = (try? NSRegularExpression(pattern: par, options: NSRegularExpressionOptions.CaseInsensitive))?.stringByReplacingMatchesInString(str, options: NSMatchingOptions.ReportCompletion, range: NSMakeRange(0, (pathInfo!).characters.count), withTemplate: str) } } if pathInfo == nil { pathInfo = "" } return pathInfo! } public var pathInfo :String?{ return nil } //the key for cache public var cacheKey :String{ if self.method == .GET { return self.url!.absoluteString.MD5 }else if !isEmpty(self.requestParams) { return (self.url!.absoluteString + self.requestParams.joinPath).MD5 }else{ return self.url!.absoluteString.MD5 } } public func suspend() { self.op?.suspend() self.state.value = .Suspend } public func resume() { self.op?.resume() self.state.value = .Sending } public func cancel() { self.op?.cancel() self.state.value = .Cancle } public var manager:Manager { get{ if let reqManager = objc_getAssociatedObject(self, &managerHandle) as? Manager { return reqManager }else if let configuration = sessionConfiguration{ let aManager = Alamofire.Manager(configuration: configuration) objc_setAssociatedObject(self, &managerHandle, aManager, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return aManager }else{ return Alamofire.Manager.sharedInstance } }set(aManager){ objc_setAssociatedObject(self, &managerHandle, aManager, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } ================================================ FILE: Pod/Classes/Easy/Core/EZScene.swift ================================================ // // Scene.swift // medical // // Created by zhuchao on 15/4/22. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import SnapKit public enum NAV : Int { case LEFT case RIGHT } public enum EXTEND : Int{ case NONE case TOP case BOTTOM case TOP_BOTTOM } public enum INSET : Int{ case NONE case TOP case BOTTOM case TOP_BOTTOM } public class EZScene: UIViewController { //parentScene 保留设计,必要的时候保存parentScene public weak var parentScene:EZScene? //导航条左右按钮设置 NAV是一个枚举值:.LEFT,.RIGHT public func showBarButton(position:NAV,title:String,fontColor:UIColor){ self.showBarButton(position, button: UIButton(navTitle: title, color: fontColor)) } public func showBarButton(position:NAV,imageName:String) -> Void{ self.showBarButton(position, button: UIButton(navImage: UIImage(named: imageName)!)) } public func showBarButton(position:NAV,button:UIButton?){ if position == .LEFT { button?.addTarget(self, action: Selector("leftButtonTouch"), forControlEvents: UIControlEvents.TouchUpInside) self.navigationItem.leftBarButtonItem = nil self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button!) self.navigationController?.interactivePopGestureRecognizer?.delegate = nil }else if position == .RIGHT { button?.addTarget(self, action: Selector("rightButtonTouch"), forControlEvents: UIControlEvents.TouchUpInside) self.navigationItem.rightBarButtonItem = nil self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button!) } } public func leftButtonTouch(){ self.navigationController?.popViewControllerAnimated(true) } public func rightButtonTouch(){ } public func setTitleView(titleView:UIView){ self.navigationItem.titleView = titleView } public func addSubView(view:UIView,extend:EXTEND){ self.view.addSubview(view) self.view.sendSubviewToBack(view) self.automaticallyAdjustsScrollViewInsets = false self.extendedLayoutIncludesOpaqueBars = true self.edgesForExtendedLayout = UIRectEdge.All view.snp_makeConstraints { (make) -> Void in //TODO // make.edges.equalTo(view.superview!).inset( // EdgeInsetsMake((extend == EXTEND.TOP||extend == EXTEND.TOP_BOTTOM) ? 64:0, left:0,bottom:(extend == EXTEND.BOTTOM||extend == EXTEND.TOP_BOTTOM) ? 49:0, right: 0) // ) } } public func addScrollView(view:UIScrollView,extend:EXTEND,inset:INSET){ self.addSubView(view, extend: extend) // view.contentInset = UIEdgeInsetsMake((inset == INSET.TOP || inset == INSET.TOP_BOTTOM) ? 64:0, 0, // (inset == INSET.BOTTOM || inset == INSET.TOP_BOTTOM) ? 49:0, 0) } } ================================================ FILE: Pod/Classes/Easy/Core/EZSceneModel.swift ================================================ // // SceneModel.swift // medical // // Created by zhuchao on 15/4/26. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond public class EZSceneModel: NSObject { } ================================================ FILE: Pod/Classes/Easy/Core/EZTableView.swift ================================================ // // EZTableView.swift // medical // // Created by zhuchao on 15/4/26. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit public class EZTableView: UITableView { /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func drawRect(rect: CGRect) { // Drawing code } */ } ================================================ FILE: Pod/Classes/Easy/Core/EZViewModel.swift ================================================ // // EZViewModel.swift // medical // // Created by zhuchao on 15/5/11. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond public class EZData:NSObject { public var dym:Observable? public var value:NSData?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ data:NSData) { self.dym = Observable(data) } } public class EZString:NSObject { public var dym:Observable? public var value:String?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ str:String) { self.dym = Observable(str) } } public class EZURL:NSObject { public var dym:Observable? public var value:NSURL?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ url:NSURL?) { self.dym = Observable(url) } } public class EZAttributedString:NSObject { public var dym:Observable? public var value:NSAttributedString?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ str:NSAttributedString) { self.dym = Observable(str) } } public class EZImage:NSObject { public var dym:Observable? public var value:UIImage?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ image:UIImage?) { self.dym = Observable(image) } } public class EZColor:NSObject { public var dym:Observable? public var value:UIColor?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ color:UIColor) { self.dym = Observable(color) } } public class EZBool:NSObject { public var dym:Observable? public var value:Bool?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ b:Bool) { self.dym = Observable(b) } } public class EZFloat:NSObject { public var dym:Observable? public var value:CGFloat?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ fl:CGFloat) { self.dym = Observable(fl) } } public class EZInt:NSObject { public var dym:Observable? public var value:Int?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ i:Int) { self.dym = Observable(i) } } public class EZNumber:NSObject { public var dym:Observable? public var value:NSNumber?{ get{ return self.dym?.value }set(value){ self.dym?.value = value! } } public init(_ i:NSNumber) { self.dym = Observable(i) } } extension NSObject{ public var model_properyies :Dictionary{ return self.listProperties() } public func model_hasKey(key:String) -> Bool{ return self.model_properyies.keys.contains(key) } } public class EZViewModel:NSObject { } ================================================ FILE: Pod/Classes/Easy/Lib/EZCoreDataManager.swift ================================================ // // EZCoreDataManager.swift // medical // // Created by zhuchao on 15/5/31. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import CoreData private var managedObjectContextHandle:UInt8 = 0 private var persistentStoreCoordinatorHandle:UInt8 = 1 private var databaseNameHandle:UInt8 = 2 private var modelNameHandle:UInt8 = 3 private var managedObjectModelHandle:UInt8 = 4 public class EZCoreDataManager { private static let appName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String public var databaseName: String { get { if let db = objc_getAssociatedObject(self, &databaseNameHandle) as? String { return db } else { return EZCoreDataManager.appName + ".sqlite" } } set(value){ objc_setAssociatedObject(self, &databaseNameHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &managedObjectContextHandle, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &persistentStoreCoordinatorHandle, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public var modelName: String { get { if let model = objc_getAssociatedObject(self, &modelNameHandle) as? String { return model } else { return EZCoreDataManager.appName } } set(value) { objc_setAssociatedObject(self, &modelNameHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &managedObjectContextHandle, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &persistentStoreCoordinatorHandle, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public var managedObjectContext: NSManagedObjectContext { get { if let context = objc_getAssociatedObject(self, &managedObjectContextHandle) as? NSManagedObjectContext { return context } else { let c = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType) c.persistentStoreCoordinator = persistentStoreCoordinator objc_setAssociatedObject(self, &managedObjectContextHandle, c, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return c } } set (value){ objc_setAssociatedObject(self, &managedObjectContextHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public var persistentStoreCoordinator: NSPersistentStoreCoordinator { get { if let store = objc_getAssociatedObject(self, &persistentStoreCoordinatorHandle) as? NSPersistentStoreCoordinator { return store } else { let p = self.persistentStoreCoordinator(NSSQLiteStoreType, storeURL: self.sqliteStoreURL) objc_setAssociatedObject(self, &persistentStoreCoordinatorHandle, p, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return p } }set(value){ objc_setAssociatedObject(self, &persistentStoreCoordinatorHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public var managedObjectModel: NSManagedObjectModel { if let m = objc_getAssociatedObject(self, &managedObjectModelHandle) as? NSManagedObjectModel { return m } else { let modelURL = NSBundle.mainBundle().URLForResource(self.modelName, withExtension: "momd") let model = NSManagedObjectModel(contentsOfURL: modelURL!) objc_setAssociatedObject(self, &managedObjectModelHandle, model, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return model! } } public func useInMemoryStore() { persistentStoreCoordinator = self.persistentStoreCoordinator(NSInMemoryStoreType, storeURL: nil) } public func saveContext() -> Bool { return self.managedObjectContext.saveData() } private var sqliteStoreURL: NSURL { #if os(iOS) let dir = EZCoreDataManager.applicationDocumentsDirectory #else let dir = EZCoreDataManager.applicationSupportDirectory self.createApplicationSupportDirIfNeeded(dir) #endif return dir!.URLByAppendingPathComponent(self.databaseName) } private func persistentStoreCoordinator(storeType: String, storeURL: NSURL?) -> NSPersistentStoreCoordinator { let c = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let error = NSErrorPointer() do { try c.addPersistentStoreWithType(storeType, configuration: nil, URL: storeURL, options: [NSMigratePersistentStoresAutomaticallyOption:true,NSInferMappingModelAutomaticallyOption:true]) } catch let error1 as NSError { error.memory = error1 print("ERROR WHILE CREATING PERSISTENT STORE COORDINATOR! " + error.debugDescription) } return c } private static var applicationDocumentsDirectory:NSURL? { return NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last } private static var applicationSupportDirectory:NSURL? { return NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.ApplicationSupportDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last?.URLByAppendingPathComponent(EZCoreDataManager.appName) } private static func createApplicationSupportDirIfNeeded(dir: NSURL) { if NSFileManager.defaultManager().fileExistsAtPath(dir.absoluteString) { return } do { try NSFileManager.defaultManager().createDirectoryAtURL(dir, withIntermediateDirectories: true, attributes: nil) } catch _ { } } // singleton public static let sharedManager = EZCoreDataManager() } public extension NSManagedObjectContext { public static var defaultContext: NSManagedObjectContext { return EZCoreDataManager.sharedManager.managedObjectContext } func createFetchRequest(entityName:String) -> NSFetchRequest { let request = NSFetchRequest() request.entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: self) return request } func saveData() -> Bool { if !self.hasChanges { return true } let error = NSErrorPointer() let save: Bool do { try self.save() save = true } catch let error1 as NSError { error.memory = error1 save = false } if (!save) { print("Unresolved error in saving context for entity:") print(self) print("!\nError: " + error.debugDescription) return false } return true } } public extension NSPredicate{ public func condition(condition: AnyObject?) -> NSPredicate?{ if let cond: AnyObject = condition { return NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates:[self, NSPredicate.predicate(cond)]) } return self } public func orCondition(condition: AnyObject?) -> NSPredicate?{ if let cond: AnyObject = condition { return NSCompoundPredicate(type: NSCompoundPredicateType.OrPredicateType, subpredicates:[self, NSPredicate.predicate(cond)]) } return self } private static func predicate(properties: [String:AnyObject]) -> NSPredicate { var preds = [NSPredicate]() for (key, value) in properties { preds.append(NSPredicate(format: "%K = %@", argumentArray: [key, value])) } return NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: preds) } private static func predicate(condition: AnyObject) -> NSPredicate { if condition is NSPredicate { return condition as! NSPredicate } if let d = condition as? [String:AnyObject] { return self.predicate(d) } return NSPredicate() } } public extension NSFetchRequest{ public func condition(condition: AnyObject?) -> NSFetchRequest{ if let cond: AnyObject = condition { if let pred = self.predicate { self.predicate = pred.condition(cond) }else{ self.predicate = NSPredicate.predicate(cond) } } return self } public func orCondition(condition: AnyObject?) -> NSFetchRequest{ if let cond: AnyObject = condition,let pred = self.predicate { self.predicate = pred.orCondition(cond) } return self } public func orderBy(key:String,_ order:String = "ASC") -> NSFetchRequest{ let sortDescriptor = NSSortDescriptor(key: key, ascending: order.uppercaseString=="ASC") if self.sortDescriptors == nil{ self.sortDescriptors = [sortDescriptor] }else{ self.sortDescriptors?.append(sortDescriptor) } return self } /** * Set the "limit" value of the query. * * @param int value * @return self * @static */ public func limit(value:Int) -> NSFetchRequest{ self.fetchLimit = value self.fetchOffset = 0 return self } /** * Alias to set the "limit" value of the query. * * @param int value * @return NSFetchRequest */ public func take(value:Int) -> NSFetchRequest{ return self.limit(value) } /** * Set the limit and offset for a given page. * * @param int page * @param int perPage * @return NSFetchRequest */ public func forPage(page:Int,_ perPage:Int) -> NSFetchRequest{ self.fetchLimit = perPage self.fetchOffset = (page - 1) * perPage return self } public func first() -> NSManagedObject?{ return self.take(1).get().first } public func delete() -> NSInteger{ var i = 0 for o in self.get() { o.delete() i++ } return i } public func get() -> [NSManagedObject]{ return (try! NSManagedObjectContext.defaultContext.executeFetchRequest(self)) as! [NSManagedObject] } public func count() -> Int { return NSManagedObjectContext.defaultContext.countForFetchRequest(self, error: nil) } public func exists() -> Bool { return self.count() > 0 } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+Array.swift ================================================ // // EZExtend+Array.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation extension Array { func stringAtIndex(index:Int,other:String) -> String{ if self.count >= index + 1 { return (self[index] as! String).trim }else{ return other } } func floatAtIndex(index:Int,other:CGFloat) -> CGFloat{ if self.count >= index + 1 { return (self[index] as! String).trim.floatValue }else{ return other } } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+Bond.swift ================================================ // // EZBond.swift // medical // // Created by zhuchao on 15/4/27. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Bond import TTTAttributedLabel @objc class TapGestureDynamicHelper:NSObject { weak var view: UIView? let sink: (UITapGestureRecognizer) -> Void init(view: UIView,number:NSInteger,sink:((UITapGestureRecognizer) -> Void)) { self.view = view self.sink = sink super.init() view.addTapGesture(number, target: self, action: Selector("tapHandle:")) } func tapHandle(gesture: UITapGestureRecognizer) { sink(gesture) } } @objc class PanGestureDynamicHelper:NSObject{ weak var view: UIView? let sink: (UIPanGestureRecognizer) -> Void init(view: UIView,number:NSInteger,sink:((UIPanGestureRecognizer) -> Void)) { self.view = view self.sink = sink super.init() view.addPanGesture(self, action: Selector("panHandle:")) } func panHandle(gestureRecognizer:UIPanGestureRecognizer) { sink(gestureRecognizer) } } @objc class SwipeGestureDynamicHelper:NSObject { weak var view: UIView? let sink: ((UISwipeGestureRecognizer) -> Void) var number:NSInteger = 1 init(view: UIView,number:NSInteger,sink:((UISwipeGestureRecognizer) -> Void)) { self.view = view self.number = number self.sink = sink super.init() view.addSwipeGesture(UISwipeGestureRecognizerDirection.Right, numberOfTouches: number, target: self, action: Selector("swipeRightHandle:")) view.addSwipeGesture(UISwipeGestureRecognizerDirection.Up, numberOfTouches: number, target: self, action: Selector("swipeUpHandle:")) view.addSwipeGesture(UISwipeGestureRecognizerDirection.Down, numberOfTouches: number, target: self, action: Selector("swipeDownHandle:")) view.addSwipeGesture(UISwipeGestureRecognizerDirection.Left, numberOfTouches: number, target: self, action: Selector("swipeLeftHandle:")) } func swipeRightHandle(gestureRecognizer:UISwipeGestureRecognizer) { sink(gestureRecognizer) } func swipeUpHandle(gestureRecognizer:UISwipeGestureRecognizer) { sink(gestureRecognizer) } func swipeDownHandle(gestureRecognizer:UISwipeGestureRecognizer) { sink(gestureRecognizer) } func swipeLeftHandle(gestureRecognizer:UISwipeGestureRecognizer) { sink(gestureRecognizer) } } extension UIView{ private struct AssociatedKeys { static var PanGestureEventKey = "bnd_PanGestureEventKey" static var PanGestureEventHelperKey = "bnd_PanGestureEventHelperKey" static var SwipeGestureEventKey = "bnd_SwipeGestureEventKey" static var SwipeGestureEventHelperKey = "bnd_SwipeGestureEventHelperKey" static var TapGestureEventKey = "bnd_TapGestureEventKey" static var TapGestureEventHelperKey = "bnd_TapGestureEventHelperKey" } public func bnd_swipeGestureEvent(number:NSInteger) ->EventProducer { if let bnd_swipeGestureEvent: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.SwipeGestureEventKey) { return bnd_swipeGestureEvent as! EventProducer } else { var capturedSink: (UISwipeGestureRecognizer -> ())! = nil let bnd_swipeGestureEvent = EventProducer { sink in capturedSink = sink return nil } let controlHelper = SwipeGestureDynamicHelper(view: self, number: number, sink: capturedSink) objc_setAssociatedObject(self, &AssociatedKeys.SwipeGestureEventHelperKey, controlHelper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &AssociatedKeys.SwipeGestureEventKey, bnd_swipeGestureEvent, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return bnd_swipeGestureEvent } } public func bnd_tapGestureEvent(number:NSInteger) ->EventProducer { if let bnd_tapGestureEvent: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.TapGestureEventKey) { return bnd_tapGestureEvent as! EventProducer } else { var capturedSink: (UITapGestureRecognizer -> ())! = nil let bnd_tapGestureEvent = EventProducer { sink in capturedSink = sink return nil } let controlHelper = TapGestureDynamicHelper(view: self, number: number, sink: capturedSink) objc_setAssociatedObject(self, &AssociatedKeys.TapGestureEventHelperKey, controlHelper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &AssociatedKeys.TapGestureEventKey, bnd_tapGestureEvent, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return bnd_tapGestureEvent } } public var bnd_panGestureEvent:EventProducer { if let bnd_panGestureEvent: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.PanGestureEventKey) { return bnd_panGestureEvent as! EventProducer } else { var capturedSink: (UIPanGestureRecognizer -> ())! = nil let bnd_panGestureEvent = EventProducer { sink in capturedSink = sink return nil } let controlHelper = PanGestureDynamicHelper(view: self, number: 1, sink: capturedSink) objc_setAssociatedObject(self, &AssociatedKeys.PanGestureEventHelperKey, controlHelper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &AssociatedKeys.PanGestureEventKey, bnd_panGestureEvent, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return bnd_panGestureEvent } } } extension UIImageView { private struct AssociatedKeys { static var UrlImageDynamicHandleUIImageView = "UrlImageDynamicHandleUIImageView" } public var bnd_URLImage: Observable { if let d: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.UrlImageDynamicHandleUIImageView) { return (d as? Observable)! } else { let d = Observable(NSURL()) d.observe { [weak self] v in if let s = self { if v != nil { s.kf_setImageWithURL(v!) } } } objc_setAssociatedObject(self, &AssociatedKeys.UrlImageDynamicHandleUIImageView, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } } } extension TTTAttributedLabel { private struct AssociatedKeys { static var textDynamicHandleTTTAttributeLabel = "textDynamicHandleTTTAttributeLabel" static var attributedTextDynamicHandleTTTAttributeLabel = "attributedTextDynamicHandleTTTAttributeLabel" static var dataDynamicHandleTTTAttributeLabel = "dataDynamicHandleTTTAttributeLabel" } public var dynTTText: Observable { if let d: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.textDynamicHandleTTTAttributeLabel) { return (d as? Observable)! } else { let d = Observable(self.text ?? "") d.observe { [weak self] v in if let s = self { s.setText(v) } } objc_setAssociatedObject(self, &AssociatedKeys.textDynamicHandleTTTAttributeLabel, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } } public var dynTTTData: Observable { if let d: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.dataDynamicHandleTTTAttributeLabel) { return (d as? Observable)! } else { let d = Observable(NSData()) d.observe{ [weak self] v in if let s = self { s.setText(NSAttributedString(fromHTMLData: v, attributes: ["dict":s.tagProperty.style])) }} objc_setAssociatedObject(self, &AssociatedKeys.dataDynamicHandleTTTAttributeLabel, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } } public var dynTTTAttributedText: Observable { if let d: AnyObject = objc_getAssociatedObject(self, &AssociatedKeys.attributedTextDynamicHandleTTTAttributeLabel) { return (d as? Observable)! } else { let d = Observable(self.attributedText ?? NSAttributedString(string: "")) d.observe { [weak self] v in if let s = self { s.setText(v) } } objc_setAssociatedObject(self, &AssociatedKeys.attributedTextDynamicHandleTTTAttributeLabel, d, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return d } } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+Dictionary.swift ================================================ // // Dictionary+EZExtend.swift // medical // // Created by zhuchao on 15/4/25. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation extension Dictionary { //join the Dictionary with "&" to a url path var joinPath : String{ var array = ArraySlice() for (key,value) in self { array.append("\(key)=\(value)") } return array.joinWithSeparator("&") } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+NSDate.swift ================================================ // // EZExtend+NSDate.swift // medical // // Created by zhuchao on 15/5/9. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation extension NSDate { //format :yyyy-MM-dd public func formatTo(format:String) -> String{ let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = format let currentDateStr = dateFormatter.stringFromDate(self) return currentDateStr } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+String.swift ================================================ // // EZExtend+String.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation // MARK: - String public func trimToArray (str:String) -> Array{ return str.trim.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).filter(){ return !$0.characters.isEmpty } } public func trimToArrayBy (str:String,by:String) -> Array{ return str.trim.componentsSeparatedByString(by).filter(){ return !$0.characters.isEmpty } } extension String { public subscript (i: Int) -> String { return String(Array(self.characters)[i]) } public var urlencode :String? { return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.letterCharacterSet()) } public var urldecode :String? { return self.stringByRemovingPercentEncoding; } public var trim :String { return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) } public var trimArray : Array{ return trimToArray(self) } public func trimArrayBy (str:String) -> Array { return trimToArrayBy(self,by: str) } public var toKeyPath :String { let keyArray = self.trim.componentsSeparatedByString("-") var str = "" var index = 0 for akey in keyArray { if index == 0 { str += akey }else{ str += akey.capitalizedString } index++ } if let reg = Regex("_").replace(str, withTemplate: ".") { str = reg } return str } public var floatValue: CGFloat { return CGFloat((self as NSString).floatValue) } public var integerValue: Int { return (self as NSString).integerValue } public var boolValue: Bool { return (self as NSString).boolValue } public func anyValue(key:String) -> AnyObject{ if key == "font" { if let font = UIFont.Font(self.trim) { return font } } return self.anyValue } public var anyValue :AnyObject{ let str = self.trim if str.hasSuffix(".cg"){ if let color = UIColor(CSS: Regex(".cg").replace(str, withTemplate: "")){ return color.CGColor } } if ["YES","NO","TRUE","FALSE"].contains(str.uppercaseString) { return str.boolValue }else if let color = UIColor(CSS: str){ return color }else if let image = UIImage(named: str){ return image }else if str.floatValue != 0.0 { return str.floatValue }else{ return str } } public var MD5 : String { let data = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) return data!.MD5String } public func toData () -> NSData?{ return self.dataUsingEncoding(NSUTF8StringEncoding)?.dataByReplacingOccurrencesOfData("\\n".dataUsingEncoding(NSUTF8StringEncoding), withData: "\n".dataUsingEncoding(NSUTF8StringEncoding)) } public var chineseFirstLetter : String{ return HTFirstLetter.firstLetter(self) } public var chineseFirstLetters : String { return HTFirstLetter.firstLetters(self) } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIAlertController.swift ================================================ // // EZExtend+UIAlertController.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit // MARK: - UIAlertController public func alert (title: String, message: String, cancelAction: ((UIAlertAction!)->Void)? = nil, okAction: ((UIAlertAction!)->Void)? = nil) -> UIAlertController { let a = UIAlertController (title: title, message: message, preferredStyle: .Alert) if let ok = okAction { a.addAction(UIAlertAction(title: "OK", style: .Default, handler: ok)) a.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: cancelAction)) } else { a.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: cancelAction)) } return a } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIButton.swift ================================================ // // UIButton+EZExtend.swift // medical // // Created by zhuchao on 15/4/22. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit let NAV_BAR_HEIGHT = CGFloat(40.0) let NAV_BUTTON_MIN_WIDTH = CGFloat(40.0) let NAV_BUTTON_MIN_HEIGHT = CGFloat(40.0) extension UIButton{ public convenience init(navImage: UIImage){ var buttonFrame = CGRectMake(CGFloat(0.0), CGFloat(0.0), navImage.size.width, NAV_BAR_HEIGHT) if buttonFrame.size.width < NAV_BUTTON_MIN_WIDTH { buttonFrame.size.width = NAV_BUTTON_MIN_WIDTH } if buttonFrame.size.height < NAV_BUTTON_MIN_HEIGHT { buttonFrame.size.height = NAV_BUTTON_MIN_HEIGHT } self.init(frame:buttonFrame) self.contentMode = UIViewContentMode.ScaleAspectFill self.backgroundColor = UIColor.clearColor() self.setImage(navImage, forState: UIControlState.Normal) } public convenience init(navTitle: String,color: UIColor){ let titleSize = navTitle.boundingRectWithSize(CGSizeMake(999999.0, NAV_BAR_HEIGHT), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: Dictionary(dictionaryLiteral: (NSFontAttributeName,UIFont.systemFontOfSize(16.0))), context: nil).size var buttonFrame = CGRectMake(CGFloat(0.0), CGFloat(0.0), titleSize.width, NAV_BAR_HEIGHT) if buttonFrame.size.width < NAV_BUTTON_MIN_WIDTH { buttonFrame.size.width = NAV_BUTTON_MIN_WIDTH } if buttonFrame.size.height < NAV_BUTTON_MIN_HEIGHT { buttonFrame.size.height = NAV_BUTTON_MIN_HEIGHT } self.init(frame: buttonFrame) self.contentMode = UIViewContentMode.ScaleAspectFill self.backgroundColor = UIColor.clearColor() self.titleLabel?.font = UIFont.systemFontOfSize(16.0) self.setTitleColor(color, forState: UIControlState.Normal) self.setTitle(navTitle, forState: UIControlState.Normal) } public func setBackgroundImageWithColor(color: UIColor?, forState state: UIControlState){ if let c = color { let image = UIImage.imageWithColor(c, size: CGSizeMake(1, 1)) self.setBackgroundImage(image, forState: state) } } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIFont.swift ================================================ // // EZExtend+UIFont.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit // MARK: - UIFont extension UIFont { public enum FontType: String { case Regular = "Regular" case Bold = "Bold" case Light = "Light" case UltraLight = "UltraLight" case Italic = "Italic" case Thin = "Thin" case Book = "Book" case Roman = "Roman" case Medium = "Medium" case MediumItalic = "MediumItalic" case CondensedMedium = "CondensedMedium" case CondensedExtraBold = "CondensedExtraBold" case SemiBold = "SemiBold" case BoldItalic = "BoldItalic" case Heavy = "Heavy" } public enum FontName: String { case HelveticaNeue = "HelveticaNeue" case Helvetica = "Helvetica" case Futura = "Futura" case Menlo = "Menlo" case Avenir = "Avenir" case AvenirNext = "AvenirNext" case Didot = "Didot" case AmericanTypewriter = "AmericanTypewriter" case Baskerville = "Baskerville" case Geneva = "Geneva" case GillSans = "GillSans" case SanFranciscoDisplay = "SanFranciscoDisplay" case Seravek = "Seravek" } public class func PrintFontFamily (font: FontName) { let arr = UIFont.fontNamesForFamilyName(font.rawValue) for name in arr { print(name) } } public class func Font (str:String) -> UIFont? { var array = str.trimArray if array.count >= 2{ let font = array[1] as String if font == "system" { return UIFont.systemFontOfSize(array[0].floatValue) }else{ return UIFont(name: array[1], size: array[0].floatValue) } }else if array.count == 1{ return UIFont.systemFontOfSize(array[0].floatValue) } return nil } public class func Font (name: FontName, type: FontType, size: CGFloat) -> UIFont { return UIFont (name: name.rawValue + "-" + type.rawValue, size: size)! } public class func HelveticaNeue (type: FontType, size: CGFloat) -> UIFont { return Font(.HelveticaNeue, type: type, size: size) } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIImage.swift ================================================ // // EZExtend+UIImage.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit // MARK: - UIImage extension UIImage { public class func imageWithColor(color:UIColor,size:CGSize) -> UIImage { let rect = CGRectMake(0, 0, size.width, size.height) UIGraphicsBeginImageContextWithOptions(size, false, 0) color.setFill() UIRectFill(rect) let image: UIImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } public func aspectResizeWithWidth (width: CGFloat) -> UIImage { let aspectSize = CGSize (width: width, height: aspectHeightForWidth(width)) UIGraphicsBeginImageContext(aspectSize) self.drawInRect(CGRect(origin: CGPointZero, size: aspectSize)) let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img } public func aspectResizeWithHeight (height: CGFloat) -> UIImage { let aspectSize = CGSize (width: aspectWidthForHeight(height), height: height) UIGraphicsBeginImageContext(aspectSize) self.drawInRect(CGRect(origin: CGPointZero, size: aspectSize)) let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img } public func aspectHeightForWidth (width: CGFloat) -> CGFloat { return (width * self.size.height) / self.size.width } public func aspectWidthForHeight (height: CGFloat) -> CGFloat { return (height * self.size.width) / self.size.height } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UILabel.swift ================================================ // // EZExtend+UILabel.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit // MARK: - UILabel private var UILabelAttributedStringArray: UInt8 = 0 extension UILabel { public var attributedStrings: [NSAttributedString]? { get { return objc_getAssociatedObject(self, &UILabelAttributedStringArray) as? [NSAttributedString] } set (value) { objc_setAssociatedObject(self, &UILabelAttributedStringArray, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public func addAttributedString (text: String, color: UIColor, font: UIFont) { let att = NSAttributedString (string: text, attributes: [NSFontAttributeName: font, NSForegroundColorAttributeName: color]) self.addAttributedString(att) } public func addAttributedString (attributedString: NSAttributedString) { var att: NSMutableAttributedString? if let a = self.attributedText { att = NSMutableAttributedString (attributedString: a) att?.appendAttributedString(attributedString) } else { att = NSMutableAttributedString (attributedString: attributedString) attributedStrings = [] } attributedStrings?.append(attributedString) self.attributedText = NSAttributedString (attributedString: att!) } public func updateAttributedStringAtIndex (index: Int, attributedString: NSAttributedString) { if (attributedStrings?[index] != nil) { attributedStrings?.removeAtIndex(index) attributedStrings?.insert(attributedString, atIndex: index) let updated = NSMutableAttributedString () for att in attributedStrings! { updated.appendAttributedString(att) } self.attributedText = NSAttributedString (attributedString: updated) } } public func updateAttributedStringAtIndex (index: Int, newText: String) { if let att = attributedStrings?[index] { let newAtt = NSMutableAttributedString (string: newText) att.enumerateAttributesInRange(NSMakeRange(0, att.string.characters.count-1), options: NSAttributedStringEnumerationOptions.LongestEffectiveRangeNotRequired, usingBlock: { (attribute, range, stop) -> Void in for (key, value) in attribute { newAtt.addAttribute(key , value: value, range: range) } }) updateAttributedStringAtIndex(index, attributedString: newAtt) } } public func getEstimatedHeight () -> CGFloat { let att = NSAttributedString(string: self.text!, attributes: [NSFontAttributeName:font]) let rect = att.boundingRectWithSize(CGSize (width: w, height: CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil) return rect.height } public func fitHeight () { self.h = getEstimatedHeight() } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIView.swift ================================================ // // EZExtend+UIView.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond // MARK: - UIView let UIViewAnimationDuration: NSTimeInterval = 1 let UIViewAnimationSpringDamping: CGFloat = 0.5 let UIViewAnimationSpringVelocity: CGFloat = 0.5 var UIViewGestureUniqueArray:CGFloat = 0.6 extension UIView { // public var gestureUniqueArray: [String:DisposableType] { // get { // if let viewGestureUniqueArray: AnyObject = objc_getAssociatedObject(self, &UIViewGestureUniqueArray) { // return viewGestureUniqueArray as! Dictionary // } else { // let mapData = [String:DisposableType]() // objc_setAssociatedObject(self, &UIViewGestureUniqueArray, mapData, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) // return mapData // } // }set(value){ //// if let viewGestureUniqueArray: AnyObject = objc_getAssociatedObject(self, &UIViewGestureUniqueArray) { //// //// } else { //// let mapData = [String:DisposableType]() //// mapData[] //// } // // } // // } // MARK: Custom Initilizer convenience init (x: CGFloat, y: CGFloat, w: CGFloat, h: CGFloat) { self.init (frame: CGRect (x: x, y: y, width: w, height: h)) } // MARK: Frame Extensions public var x: CGFloat { get { return self.frame.origin.x } set (value) { self.frame = CGRect (x: value, y: self.y, width: self.w, height: self.h) } } public var y: CGFloat { get { return self.frame.origin.y } set (value) { self.frame = CGRect (x: self.x, y: value, width: self.w, height: self.h) } } public var w: CGFloat { get { return self.frame.size.width } set (value) { self.frame = CGRect (x: self.x, y: self.y, width: value, height: self.h) } } public var h: CGFloat { get { return self.frame.size.height } set (value) { self.frame = CGRect (x: self.x, y: self.y, width: self.w, height: value) } } public var left: CGFloat { get { return self.x } set (value) { self.x = value } } var right: CGFloat { get { return self.x + self.w } set (value) { self.x = value - self.w } } public var top: CGFloat { get { return self.y } set (value) { self.y = value } } public var bottom: CGFloat { get { return self.y + self.h } set (value) { self.y = value - self.h } } public var position: CGPoint { get { return self.frame.origin } set (value) { self.frame = CGRect (origin: value, size: self.frame.size) } } public var size: CGSize { get { return self.frame.size } set (value) { self.frame = CGRect (origin: self.frame.origin, size: size) } } public func leftWithOffset (offset: CGFloat) -> CGFloat { return self.left - offset } public func rightWithOffset (offset: CGFloat) -> CGFloat { return self.right + offset } public func topWithOffset (offset: CGFloat) -> CGFloat { return self.top - offset } public func botttomWithOffset (offset: CGFloat) -> CGFloat { return self.bottom + offset } // MARK: Transform Extensions public func setRotationX (x: CGFloat) { var transform = CATransform3DIdentity transform.m34 = 1.0 / -1000.0 transform = CATransform3DRotate(transform, degreesToRadians(x), 1.0, 0.0, 0.0) self.layer.transform = transform } public func setRotationY (y: CGFloat) { var transform = CATransform3DIdentity transform.m34 = 1.0 / -1000.0 transform = CATransform3DRotate(transform, degreesToRadians(y), 0.0, 1.0, 0.0) self.layer.transform = transform } public func setRotationZ (z: CGFloat) { var transform = CATransform3DIdentity transform.m34 = 1.0 / -1000.0 transform = CATransform3DRotate(transform, degreesToRadians(z), 0.0, 0.0, 1.0) self.layer.transform = transform } public func setRotation (x: CGFloat, y: CGFloat, z: CGFloat) { var transform = CATransform3DIdentity transform.m34 = 1.0 / -1000.0 transform = CATransform3DRotate(transform, degreesToRadians(x), 1.0, 0.0, 0.0) transform = CATransform3DRotate(transform, degreesToRadians(y), 0.0, 1.0, 0.0) transform = CATransform3DRotate(transform, degreesToRadians(z), 0.0, 0.0, 1.0) self.layer.transform = transform } public func setScale (x: CGFloat, y: CGFloat) { var transform = CATransform3DIdentity transform.m34 = 1.0 / -1000.0 transform = CATransform3DScale(transform, x, y, 1) self.layer.transform = transform } // MARK: Anchor Extensions public func setAnchorPosition (anchorPosition: AnchorPosition) { print(anchorPosition.rawValue) self.layer.anchorPoint = anchorPosition.rawValue } // MARK: Layer Extensions public func addShadow (offset: CGSize, radius: CGFloat, color: UIColor, opacity: Float) { self.layer.shadowOffset = offset self.layer.shadowRadius = radius self.layer.shadowOpacity = opacity self.layer.shadowColor = color.CGColor } public func addBorder (width: CGFloat, color: UIColor) { self.layer.borderWidth = width self.layer.borderColor = color.CGColor self.layer.masksToBounds = true } public func setCornerRadius (radius: CGFloat) { self.layer.cornerRadius = radius self.layer.masksToBounds = true } public func drawCircle (fillColor: UIColor, strokeColor: UIColor, strokeWidth: CGFloat) { let path = UIBezierPath (roundedRect: CGRect (x: 0, y: 0, width: self.w, height: self.w), cornerRadius: self.w/2) let shapeLayer = CAShapeLayer () shapeLayer.path = path.CGPath shapeLayer.fillColor = fillColor.CGColor shapeLayer.strokeColor = strokeColor.CGColor shapeLayer.lineWidth = strokeWidth self.layer.addSublayer(shapeLayer) } public func drawStroke (width: CGFloat, color: UIColor) { let path = UIBezierPath (roundedRect: CGRect (x: 0, y: 0, width: self.w, height: self.w), cornerRadius: self.w/2) let shapeLayer = CAShapeLayer () shapeLayer.path = path.CGPath shapeLayer.fillColor = UIColor.clearColor().CGColor shapeLayer.strokeColor = color.CGColor shapeLayer.lineWidth = width self.layer.addSublayer(shapeLayer) } public func drawArc (from: CGFloat, to: CGFloat, clockwise: Bool, width: CGFloat, fillColor: UIColor, strokeColor: UIColor, lineCap: String) { let path = UIBezierPath (arcCenter: self.center, radius: self.w/2, startAngle: degreesToRadians(from), endAngle: degreesToRadians(to), clockwise: clockwise) let shapeLayer = CAShapeLayer () shapeLayer.path = path.CGPath shapeLayer.fillColor = fillColor.CGColor shapeLayer.strokeColor = strokeColor.CGColor shapeLayer.lineWidth = width self.layer.addSublayer(shapeLayer) } // MARK: Animation Extensions public func spring (animations: (()->Void)!, completion: ((Bool)->Void)? = nil) { UIView.animateWithDuration(UIViewAnimationDuration, delay: 0, usingSpringWithDamping: UIViewAnimationSpringDamping, initialSpringVelocity: UIViewAnimationSpringVelocity, options: UIViewAnimationOptions.AllowAnimatedContent, animations: animations, completion: completion) } public func animate (animations: (()->Void)!, completion: ((Bool)->Void)? = nil) { UIView.animateWithDuration(UIViewAnimationDuration, animations: animations, completion: completion) } // MARK: Gesture Extensions public func addTapGesture (tapNumber: NSInteger, target: AnyObject, action: Selector) { let tap = UITapGestureRecognizer (target: target, action: action) tap.numberOfTapsRequired = tapNumber self.addGestureRecognizer(tap) } public func addSwipeGesture (direction: UISwipeGestureRecognizerDirection, numberOfTouches: Int, target: AnyObject, action: Selector) { let swipe = UISwipeGestureRecognizer (target: target, action: action) swipe.direction = direction swipe.numberOfTouchesRequired = numberOfTouches self.addGestureRecognizer(swipe) } public func addPanGesture (target: AnyObject, action: Selector) { let pan = UIPanGestureRecognizer (target: target, action: action) self.addGestureRecognizer(pan) } public func whenTap(number:NSInteger = 1,block:()->Void) -> DisposableType{ self.userInteractionEnabled = true return bnd_tapGestureEvent(number).observe { (_) -> () in block() } } public func whenSwipe(number:NSInteger = 1,direction:UISwipeGestureRecognizerDirection,block:()->Void) -> DisposableType{ self.userInteractionEnabled = true return bnd_swipeGestureEvent(number).observe { (_) -> () in block() } } } ================================================ FILE: Pod/Classes/Easy/Lib/EZExtend+UIViewController.swift ================================================ // // EZExtend+UIViewController.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit // MARK: - UIViewController extension UIViewController { public var top: CGFloat { get { if let nav = self.navigationController { if nav.navigationBarHidden { return view.top } else { return nav.navigationBar.bottom } } else { return view.top } } } public var bottom: CGFloat { get { if let tab = tabBarController { if tab.tabBar.hidden { return view.bottom } else { return tab.tabBar.top } } else { return view.bottom } } } public var navigationBarHeight: CGFloat { get { if let nav = self.navigationController { return nav.navigationBar.h } return 0 } } public var navigationBarColor: UIColor? { get { return navigationController?.navigationBar.tintColor } set (value) { navigationController?.navigationBar.barTintColor = value } } public var navigationBar: UINavigationBar? { get { return navigationController?.navigationBar } } public var applicationFrame: CGRect { get { return CGRect (x: view.x, y: top, width: view.w, height: bottom - top) } } } ================================================ FILE: Pod/Classes/Easy/Lib/EZKit.swift ================================================ // // CEMKit.swift // CEMKit-Swift // // Created by Cem Olcay on 05/11/14. // Copyright (c) 2014 Cem Olcay. All rights reserved. // import UIKit // MARK: - UIBarButtonItem public func barButtonItem (imageName: String, action: (AnyObject)->()) -> UIBarButtonItem { let button = BlockButton (frame: CGRect(x: 0, y: 0, width: 20, height: 20)) button.setImage(UIImage(named: imageName), forState: .Normal) button.actionBlock = action return UIBarButtonItem (customView: button) } public func barButtonItem (title: String, color: UIColor, action: (AnyObject)->()) -> UIBarButtonItem { let button = BlockButton (frame: CGRect(x: 0, y: 0, width: 20, height: 20)) button.setTitle(title, forState: .Normal) button.setTitleColor(color, forState: .Normal) button.actionBlock = action button.sizeToFit() return UIBarButtonItem (customView: button) } // MARK: - CGSize public func + (left: CGSize, right: CGSize) -> CGSize { return CGSize (width: left.width + right.width, height: left.height + right.height) } public func - (left: CGSize, right: CGSize) -> CGSize { return CGSize (width: left.width - right.width, height: left.width - right.width) } // MARK: - CGPoint public func + (left: CGPoint, right: CGPoint) -> CGPoint { return CGPoint (x: left.x + right.x, y: left.y + right.y) } public func - (left: CGPoint, right: CGPoint) -> CGPoint { return CGPoint (x: left.x - right.x, y: left.y - right.y) } public enum AnchorPosition: CGPoint { case TopLeft = "{0, 0}" case TopCenter = "{0.5, 0}" case TopRight = "{1, 0}" case MidLeft = "{0, 0.5}" case MidCenter = "{0.5, 0.5}" case MidRight = "{1, 0.5}" case BottomLeft = "{0, 1}" case BottomCenter = "{0.5, 1}" case BottomRight = "{1, 1}" } extension CGPoint: StringLiteralConvertible { public init(stringLiteral value: StringLiteralType) { self = CGPointFromString(value) } public init(extendedGraphemeClusterLiteral value: StringLiteralType) { self = CGPointFromString(value) } public init(unicodeScalarLiteral value: StringLiteralType) { self = CGPointFromString(value) } } // MARK: - CGFloat public func degreesToRadians (angle: CGFloat) -> CGFloat { return (CGFloat (M_PI) * angle) / 180.0 } public func normalizeValue (value: CGFloat, min: CGFloat, max: CGFloat) -> CGFloat { return (max - min) / value } public func convertNormalizedValue (value: CGFloat, min: CGFloat, max: CGFloat) -> CGFloat { return ((max - min) * value) + min } // MARK: - Block Classes // MARK: - BlockButton public class BlockButton: UIButton { override init (frame: CGRect) { super.init(frame: frame) } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } var actionBlock: ((sender: BlockButton) -> ())? { didSet { self.addTarget(self, action: "action:", forControlEvents: UIControlEvents.TouchUpInside) } } func action (sender: BlockButton) { actionBlock! (sender: sender) } } // MARK: - BlockWebView public class BlockWebView: UIWebView, UIWebViewDelegate { var didStartLoad: ((NSURLRequest?) -> ())? var didFinishLoad: ((NSURLRequest?) -> ())? var didFailLoad: ((NSURLRequest?, NSError?) -> ())? var shouldStartLoadingRequest: ((NSURLRequest) -> (Bool))? override init(frame: CGRect) { super.init(frame: frame) delegate = self } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } @objc public func webViewDidStartLoad(webView: UIWebView) { didStartLoad? (webView.request) } @objc public func webViewDidFinishLoad(webView: UIWebView) { didFinishLoad? (webView.request) } @objc public func webView(webView: UIWebView, didFailLoadWithError error: NSError?) { didFailLoad? (webView.request, error) } @objc public func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { if let should = shouldStartLoadingRequest { return should (request) } else { return true } } } public func nsValueForAny(anyValue:Any) -> NSObject? { switch(anyValue) { case let intValue as Int: return NSNumber(int: CInt(intValue)) case let doubleValue as Double: return NSNumber(double: CDouble(doubleValue)) case let stringValue as String: return stringValue as NSString case let boolValue as Bool: return NSNumber(bool: boolValue) default: return nil } } extension NSObject{ public class var nameOfClass: String{ return NSStringFromClass(self).componentsSeparatedByString(".").last! } public var nameOfClass: String{ return NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! } public func listProperties() -> Dictionary{ var modelDictionary = Dictionary() Mirror(reflecting: self).children.forEach { (element) -> () in if element.label != "super" { if let nsValue = nsValueForAny(element.value) { modelDictionary.updateValue(nsValue, forKey: element.label!) } } } return modelDictionary } } public func isEmpty(x: C) -> Bool { if x.isKindOfClass(NSNull) { return true }else if x.respondsToSelector(Selector("length")) && NSData().self.length == 0 { return true }else if x.respondsToSelector(Selector("count")) && NSArray().self.count == 0 { return true } return false } ================================================ FILE: Pod/Classes/Easy/Lib/EZPrintln.swift ================================================ // // EZPrintln.swift // medical // // Created by zhuchao on 15/4/28. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation var EZ_DEBUG_MODE = true /** Writes the textual representation of `object` and a newline character into the standard output. The textual representation is obtained from the `object` using its protocol conformances, in the following order of preference: `Streamable`, `Printable`, `DebugPrintable`. This functional also augments the original function with the filename, function name, and line number of the object that is being logged. - parameter object: A textual representation of the object. - parameter file: Defaults to the name of the file that called magic(). Do not override this default. - parameter function: Defaults to the name of the function within the file in which magic() was called. Do not override this default. - parameter line: Defaults to the line number within the file in which magic() was called. Do not override this default. */ public func EZPrintln(object: T, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) { if EZ_DEBUG_MODE { let filename = NSURL(fileURLWithPath: file).lastPathComponent?.URLString print("\(filename).\(function)[\(line)]: \(object)\n", terminator: "") } } ================================================ FILE: Pod/Classes/Easy/Lib/EZSystemInfo.swift ================================================ // // EZSystemInfo.swift // medical // // Created by zhuchao on 15/4/22. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit #if os(iOS) let IOS10_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("10.0") != NSComparisonResult.OrderedAscending) let IOS9_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("9.0") != NSComparisonResult.OrderedAscending) let IOS8_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("8.0") != NSComparisonResult.OrderedAscending) let IOS7_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("7.0") != NSComparisonResult.OrderedAscending) let IOS6_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("6.0") != NSComparisonResult.OrderedAscending) let IOS5_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("5.0") != NSComparisonResult.OrderedAscending) let IOS4_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("4.0") != NSComparisonResult.OrderedAscending) let IOS3_OR_LATER = (UIDevice.currentDevice().systemVersion.caseInsensitiveCompare("3.0") != NSComparisonResult.OrderedAscending) let IOS9_OR_EARLIER = !IOS10_OR_LATER let IOS8_OR_EARLIER = !IOS9_OR_LATER let IOS7_OR_EARLIER = !IOS8_OR_LATER let IOS6_OR_EARLIER = !IOS7_OR_LATER let IOS5_OR_EARLIER = !IOS6_OR_LATER let IOS4_OR_EARLIER = !IOS5_OR_LATER let IOS3_OR_EARLIER = !IOS4_OR_LATER let IS_SCREEN_4_INCH = CGSizeEqualToSize(CGSizeMake(640, 1136), UIScreen.mainScreen().currentMode!.size) let IS_SCREEN_35_INCH = CGSizeEqualToSize(CGSizeMake(640, 960), UIScreen.mainScreen().currentMode!.size) let IS_SCREEN_47_INCH = CGSizeEqualToSize(CGSizeMake(750, 1334), UIScreen.mainScreen().currentMode!.size) let IS_SCREEN_55_INCH = CGSizeEqualToSize(CGSizeMake(1242, 2208), UIScreen.mainScreen().currentMode!.size) #else let IOS9_OR_LATER = false let IOS8_OR_LATER = false let IOS7_OR_LATER = false let IOS6_OR_LATER = false let IOS5_OR_LATER = false let IOS4_OR_LATER = false let IOS3_OR_LATER = false let IOS9_OR_EARLIER = false let IOS8_OR_EARLIER = false let IOS7_OR_EARLIER = false let IOS6_OR_EARLIER = false let IOS5_OR_EARLIER = false let IOS4_OR_EARLIER = false let IOS3_OR_EARLIER = false let IS_SCREEN_4_INCH = false let IS_SCREEN_35_INCH = false let IS_SCREEN_47_INCH = false let IS_SCREEN_55_INCH = false #endif public var IsSimulator:Bool { #if (arch(i386) || arch(x86_64)) && os(iOS) return true; #else return false; #endif } public func OSVersion() ->String{ #if os(iOS) return UIDevice.currentDevice().systemName + " " + UIDevice.currentDevice().systemVersion #else // #if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) return "" #endif // #if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) } //func AppVersion() ->String{ // var value = NSBundle.mainBundle().infoDictionary["CFBundleVersion"] // if nil == value || 0 == value.length // { // value = NSBundle.mainBundle().infoDictionary["CFBundleShortVersion"] // } // return value //} //func AppIdentifier() ->String{ //#if os(iOS) // var value = NSBundle.mainBundle().infoDictionary["CFBundleVersion"] // if nil == value || 0 == value.length // { // value = NSBundle.mainBundle().infoDictionary["CFBundleShortVersion"] // } // return value //#else // #if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) // return "" //#endif // #if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) //} public var Orientation: UIInterfaceOrientation { get { return UIApplication.sharedApplication().statusBarOrientation } } public var ScreenWidth: CGFloat { get { if UIInterfaceOrientationIsPortrait(Orientation) { return UIScreen.mainScreen().bounds.size.width } else { return UIScreen.mainScreen().bounds.size.height } } } public var ScreenHeight: CGFloat { get { if UIInterfaceOrientationIsPortrait(Orientation) { return UIScreen.mainScreen().bounds.size.height } else { return UIScreen.mainScreen().bounds.size.width } } } public var StatusBarHeight: CGFloat { get { return UIApplication.sharedApplication().statusBarFrame.height } } ================================================ FILE: Pod/Classes/Easy/Lib/EZWatch.swift ================================================ // EZWatch.swift // medical // // Created by zhuchao on 15/4/29. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation func watchForChangesToFilePath(filePath:String,callback:dispatch_block_t) { let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) let fileDescriptor = open(filePath, O_EVTONLY) if fileDescriptor <= 0 { return } assert(fileDescriptor > 0, "Error could subscribe to events for file at path: \(filePath)") let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, UInt(fileDescriptor), DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND, queue) dispatch_source_set_event_handler(source){ let flags = dispatch_source_get_data(source) if flags != 0 { dispatch_source_cancel(source) dispatch_async(dispatch_get_main_queue()){ callback() } let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.5*Double(NSEC_PER_SEC))) dispatch_after(popTime, queue){ watchForChangesToFilePath(filePath, callback: callback) } } } dispatch_source_set_cancel_handler(source){ close(fileDescriptor) } dispatch_resume(source) } ================================================ FILE: Pod/Classes/Easy/Lib/EasyORM.swift ================================================ // // NSManagedObject+EZExtend.swift // medical // // Created by zhuchao on 15/5/30. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import CoreData public func DBQuery(aClass: NSManagedObject.Type!,entityName:String) -> NSFetchRequest { return aClass.defaultContext().createFetchRequest(entityName) } public class EasyORM { public static var generateRelationships = false public static func setUpEntities(entities: [String:NSManagedObject.Type]) { nameToEntities = entities } private static var nameToEntities: [String:NSManagedObject.Type] = [String:NSManagedObject.Type]() } public extension NSManagedObject{ public func defaultContext() -> NSManagedObjectContext{ return self.managedObjectContext ?? self.dynamicType.defaultContext() } public static func defaultContext() -> NSManagedObjectContext{ return NSManagedObjectContext.defaultContext } private static var query:NSFetchRequest{ return self.defaultContext().createFetchRequest(self.entityName()) } public static func condition(condition: AnyObject?) -> NSFetchRequest{ return self.query.condition(condition) } public static func orderBy(key:String,_ order:String = "ASC") -> NSFetchRequest{ return self.query.orderBy(key, order) } /** * Set the "limit" value of the query. * * @param int value * @return self * @static */ public static func limit(value:Int) -> NSFetchRequest{ return self.query.limit(value) } /** * Alias to set the "limit" value of the query. * * @param int value * @return NSFetchRequest */ public static func take(value:Int) -> NSFetchRequest{ return self.query.take(value) } /** * Set the limit and offset for a given page. * * @param int page * @param int perPage * @return NSFetchRequest */ public static func forPage(page:Int,_ perPage:Int) -> NSFetchRequest{ return self.query.forPage(page,perPage) } public static func all() -> [NSManagedObject] { return self.query.get() } public static func count() -> Int { return self.query.count() } public static func findAndUpdate(unique:[String:AnyObject],data:[String:AnyObject]) -> NSManagedObject?{ if let object = self.find(unique) { object.update(data) return object }else{ return nil } } public static func updateOrCreate(unique:[String:AnyObject],data:[String:AnyObject]) -> NSManagedObject{ if let object = self.find(unique) { object.update(data) return object }else{ return self.create(data) } } public static func findOrCreate(properties: [String:AnyObject]) -> NSManagedObject { let transformed = self.transformProperties(properties) let existing = self.find(properties) return existing ?? self.create(transformed) } public static func find(condition: AnyObject) -> NSManagedObject? { return self.query.condition(condition).first() } public func update(properties: [String:AnyObject]) { if (properties.count == 0) { return } let transformed = self.dynamicType.transformProperties(properties) //Finish for (key, value) in transformed { self.willChangeValueForKey(key) self.setSafeValue(value, forKey: key) self.didChangeValueForKey(key) } } public func save() -> Bool { return self.defaultContext().saveData() } public func delete() -> NSManagedObjectContext { let context = self.defaultContext() context.deleteObject(self) return context } public static func deleteAll() -> NSManagedObjectContext{ for o in self.all() { o.delete() } return self.defaultContext() } public static func create() -> NSManagedObject { let o = NSEntityDescription.insertNewObjectForEntityForName(self.entityName(), inManagedObjectContext: self.defaultContext()) if let idprop = self.autoIncrementingId() { o.setPrimitiveValue(NSNumber(integer: self.nextId()), forKey: idprop) } return o } public static func create(properties: [String:AnyObject]) -> NSManagedObject { let newEntity: NSManagedObject = self.create() newEntity.update(properties) if let idprop = self.autoIncrementingId() { if newEntity.primitiveValueForKey(idprop) == nil { newEntity.setPrimitiveValue(NSNumber(integer: self.nextId()), forKey: idprop) } } return newEntity } public static func autoIncrements() -> Bool { return self.autoIncrementingId() != nil } public static func nextId() -> Int { let key = "SwiftRecord-" + self.entityName() + "-ID" if self.autoIncrementingId() != nil { let id = NSUserDefaults.standardUserDefaults().integerForKey(key) NSUserDefaults.standardUserDefaults().setInteger(id + 1, forKey: key) return id } return 0 } public class func autoIncrementingId() -> String? { return nil } //Private private static func transformProperties(properties: [String:AnyObject]) -> [String:AnyObject]{ let entity = NSEntityDescription.entityForName(self.entityName(), inManagedObjectContext: self.defaultContext())! let attrs = entity.attributesByName let rels = entity.relationshipsByName var transformed = [String:AnyObject]() for (key, value) in properties { let localKey = self.keyForRemoteKey(key) if attrs[localKey] != nil { transformed[localKey] = value } else if let rel = rels[localKey] { if EasyORM.generateRelationships { if rel.toMany { if let array = value as? [[String:AnyObject]] { transformed[localKey] = self.generateSet(rel, array: array) } else { #if DEBUG println("Invalid value for relationship generation in \(NSStringFromClass(self)).\(localKey)") println(value) #endif } } else if let dict = value as? [String:AnyObject] { transformed[localKey] = self.generateObject(rel, dict: dict) } else { #if DEBUG println("Invalid value for relationship generation in \(NSStringFromClass(self)).\(localKey)") println(value) #endif } } } } return transformed } private func setSafeValue(value: AnyObject?, forKey key: String) { if (value == nil) { self.setNilValueForKey(key) return } let val: AnyObject = value! if let attr = self.entity.attributesByName[key] { let attrType = attr.attributeType if attrType == NSAttributeType.StringAttributeType && value is NSNumber { self.setPrimitiveValue((val as! NSNumber).stringValue, forKey: key) } else if let s = val as? String { if self.isIntegerAttributeType(attrType) { self.setPrimitiveValue(NSNumber(integer: val.integerValue), forKey: key) return } else if attrType == NSAttributeType.BooleanAttributeType { self.setPrimitiveValue(NSNumber(bool: val.boolValue), forKey: key) return } else if (attrType == NSAttributeType.FloatAttributeType) { self.setPrimitiveValue(NSNumber(floatLiteral: val.doubleValue), forKey: key) return } else if (attrType == NSAttributeType.DateAttributeType) { self.setPrimitiveValue(self.dynamicType.dateFormatter.dateFromString(s), forKey: key) return } } } self.setPrimitiveValue(value, forKey: key) } private func isIntegerAttributeType(attrType: NSAttributeType) -> Bool { return attrType == NSAttributeType.Integer16AttributeType || attrType == NSAttributeType.Integer32AttributeType || attrType == NSAttributeType.Integer64AttributeType } private static var dateFormatter: NSDateFormatter { if _dateFormatter == nil { _dateFormatter = NSDateFormatter() _dateFormatter!.dateFormat = "yyyy-MM-dd HH:mm:ss z" } return _dateFormatter! } private static var _dateFormatter: NSDateFormatter? public class func mappings() -> [String:String] { return [String:String]() } public static func keyForRemoteKey(remote: String) -> String { if let s = cachedMappings[remote] { return s } let entity = NSEntityDescription.entityForName(self.entityName(), inManagedObjectContext: self.defaultContext())! let properties = entity.propertiesByName if properties[remote] != nil { _cachedMappings![remote] = remote return remote } let camelCased = remote.camelCase if properties[camelCased] != nil { _cachedMappings![remote] = camelCased return camelCased } _cachedMappings![remote] = remote return remote } private static var cachedMappings: [String:String] { if let m = _cachedMappings { return m } else { var m = [String:String]() for (key, value) in mappings() { m[value] = key } _cachedMappings = m return m } } private static var _cachedMappings: [String:String]? private static func generateSet(rel: NSRelationshipDescription, array: [[String:AnyObject]]) -> NSSet { var cls: NSManagedObject.Type? if EasyORM.nameToEntities.count > 0 { cls = EasyORM.nameToEntities[rel.destinationEntity!.managedObjectClassName] } if cls == nil { cls = (NSClassFromString(rel.destinationEntity!.managedObjectClassName) as! NSManagedObject.Type) } else { print("Got class name from entity setup") } let set = NSMutableSet() for d in array { set.addObject(cls!.findOrCreate(d)) } return set } private static func generateObject(rel: NSRelationshipDescription, dict: [String:AnyObject]) -> NSManagedObject { let entity = rel.destinationEntity! let cls: NSManagedObject.Type = NSClassFromString(entity.managedObjectClassName) as! NSManagedObject.Type return cls.findOrCreate(dict) } public static func primaryKey() -> String { NSException(name: "Primary key undefined in " + NSStringFromClass(self), reason: "Override primaryKey if you want to support automatic creation, otherwise disable this feature", userInfo: nil).raise() return "" } private static func entityName() -> String { var name = NSStringFromClass(self) if name.rangeOfString(".") != nil { let comp = name.characters.split {$0 == "."}.map { String($0) } if comp.count > 1 { name = comp.last! } } if name.rangeOfString("_") != nil { var comp = name.characters.split {$0 == "_"}.map { String($0) } var last: String = "" var remove = -1 for (i,s) in Array(comp.reverse()).enumerate() { if last == s { remove = i } last = s } if remove > -1 { comp.removeAtIndex(remove) name = comp.joinWithSeparator("_") } } return name } } public extension String { var camelCase: String { let spaced = self.stringByReplacingOccurrencesOfString("_", withString: " ", options: [], range:Range(start: self.startIndex, end: self.endIndex)) let capitalized = spaced.capitalizedString let spaceless = capitalized.stringByReplacingOccurrencesOfString(" ", withString: "", options:[], range:Range(start:self.startIndex, end:self.endIndex)) return spaceless.stringByReplacingCharactersInRange(Range(start:spaceless.startIndex, end:spaceless.startIndex.successor()), withString: "\(spaceless[spaceless.startIndex])".lowercaseString) } } ================================================ FILE: Pod/Classes/Easy/Lib/NSData+EasyExtend.h ================================================ // // NSData+EasyExtend.h // fastSign // // Created by EasyIOS on 14-4-10. // Copyright (c) 2014年 zhuchao. All rights reserved. // #import @interface NSData (EasyExtend) @property (nonatomic, readonly) NSData * MD5; @property (nonatomic, readonly) NSString * MD5String; + (NSData *)fromResource:(NSString *)resName; @end ================================================ FILE: Pod/Classes/Easy/Lib/NSData+EasyExtend.m ================================================ // // NSData+EasyExtend.m // // Created by EasyIOS on 14-4-10. // Copyright (c) 2014年 zhuchao. All rights reserved. // #import "NSData+EasyExtend.h" #import @implementation NSData (EasyExtend) - (NSData *)MD5 { unsigned char md5Result[CC_MD5_DIGEST_LENGTH + 1]; CC_LONG md5Length = (CC_LONG)[self length]; CC_MD5( [self bytes], md5Length, md5Result ); NSMutableData * retData = [[NSMutableData alloc] init]; if ( nil == retData ) return nil; [retData appendBytes:md5Result length:CC_MD5_DIGEST_LENGTH]; return retData; } - (NSString *)MD5String { NSData * value = [self MD5]; if ( value ) { char tmp[16]; unsigned char * hex = (unsigned char *)malloc( 2048 + 1 ); unsigned char * bytes = (unsigned char *)[value bytes]; unsigned long length = [value length]; hex[0] = '\0'; for ( unsigned long i = 0; i < length; ++i ) { sprintf( tmp, "%02X", bytes[i] ); strcat( (char *)hex, tmp ); } NSString * result = [NSString stringWithUTF8String:(const char *)hex]; free( hex ); return result; } else { return nil; } } + (NSData *)fromResource:(NSString *)resName { NSString * extension = [resName pathExtension]; NSString * fullName = [resName substringToIndex:(resName.length - extension.length - 1)]; NSString * path = [[NSBundle mainBundle] pathForResource:fullName ofType:extension]; return [NSData dataWithContentsOfFile:path]; } @end ================================================ FILE: Pod/Classes/Easy/Lib/PullRefresh/EZInfiniteScrolling.swift ================================================ // // EZInfiniteScrolling.swift // Demo // // Created by zhuchao on 15/5/15. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond let EZInfiniteScrollingViewHeight:CGFloat = 60.0 public enum EZInfiniteScrollingState { case Stopped case Triggered case Loading case Pulling case Ended } public class Footer:UIView { init(scrollView:UIScrollView,frame: CGRect = CGRectZero) { super.init(frame: frame) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func resetScrollViewContentInset(scrollView:UIScrollView){ var currentInsets = scrollView.contentInset let newBottom = scrollView.infiniteScrollingView!.originalBottomInset + scrollView.infiniteScrollingView!.extendBottom; if newBottom != currentInsets.bottom { currentInsets.bottom = newBottom self.setScrollViewContentInset(currentInsets, scrollView: scrollView) } } public func setScrollViewContentInsetForLoading(scrollView:UIScrollView){ var currentInsets = scrollView.contentInset let newBottom = scrollView.infiniteScrollingView!.originalBottomInset + scrollView.infiniteScrollingView!.extendBottom + EZInfiniteScrollingViewHeight; if newBottom != currentInsets.bottom { currentInsets.bottom = newBottom self.setScrollViewContentInset(currentInsets, scrollView: scrollView) } } public func setScrollViewContentInset(contentInset:UIEdgeInsets,scrollView:UIScrollView){ UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { scrollView.contentInset = contentInset },completion:nil) } } public class EZInfiniteScrollingView :UIView { public var state = Observable(.Stopped) public var extendBottom:CGFloat = 0.0 public var originalBottomInset:CGFloat = 0.0 private var infiniteScrollingHandler:(Void -> ())? private var oldState = EZInfiniteScrollingState.Stopped private func commonInit(){ self.autoresizingMask = UIViewAutoresizing.FlexibleWidth self.state.observe{ [unowned self] state in if state == .Loading && self.oldState == .Triggered { self.infiniteScrollingHandler?() } self.oldState = state } } override init(frame: CGRect) { super.init(frame: frame) self.commonInit() } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } func setCustomView(customView:UIView){ if customView.isKindOfClass(UIView) { for view in self.subviews { view.removeFromSuperview() } self.addSubview(customView) let viewBounds = customView.bounds; let origin = CGPointMake( CGFloat(roundf(Float(self.bounds.size.width-viewBounds.size.width)/2)), CGFloat(roundf(Float(self.bounds.size.height-viewBounds.size.height)/2))) customView.frame = CGRectMake(origin.x, origin.y, viewBounds.size.width, viewBounds.size.height) } } public func resetState(){ self.state.value = .Stopped; } public func startAnimating(){ self.state.value = .Loading; } public func stopAnimating(){ self.state.value = .Stopped; } public func setEnded(){ self.state.value = .Ended; } public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { let scrollView = object as! UIScrollView if keyPath == "contentOffset" && scrollView.showsInfiniteScrolling { if self.state.value != .Loading && self.state.value != .Ended && scrollView.contentSize.height > 0{ let scrollViewContentHeight = scrollView.contentSize.height; let scrollOffsetThreshold = scrollViewContentHeight - scrollView.bounds.size.height + self.extendBottom if !scrollView.dragging && self.state.value == .Triggered { self.state.value = .Loading }else if scrollView.dragging && self.state.value == .Pulling && scrollView.contentOffset.y - scrollOffsetThreshold > EZInfiniteScrollingViewHeight { self.state.value = .Triggered }else if scrollView.contentOffset.y - scrollOffsetThreshold <= 1 && self.state.value != .Stopped { self.state.value = .Stopped }else if scrollView.contentOffset.y - scrollOffsetThreshold > 0 && scrollView.contentOffset.y - scrollOffsetThreshold < EZInfiniteScrollingViewHeight { self.state.value = .Pulling } } }else if keyPath == "contentSize" && scrollView.contentSize.height >= scrollView.bounds.size.height && scrollView.showsInfiniteScrolling == false { scrollView.showsInfiniteScrolling = true // 当contentSize.height大于scrollView的高度时才显示上拉加载 } } } private var InfiniteScrollingViewHandle :UInt8 = 1 extension UIScrollView { public var infiniteScrollingView : EZInfiniteScrollingView? { get{ if let d: AnyObject = objc_getAssociatedObject(self, &InfiniteScrollingViewHandle) { return d as? EZInfiniteScrollingView }else{ return nil } }set (value){ objc_setAssociatedObject(self, &InfiniteScrollingViewHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var showsInfiniteScrolling :Bool { get{ return !self.infiniteScrollingView!.hidden }set(value){ if value { self.addObserver(self.infiniteScrollingView!, forKeyPath: "contentOffset", options:NSKeyValueObservingOptions.New, context: nil) self.infiniteScrollingView?.hidden = false }else if (self.infiniteScrollingView?.hidden == false){ self.removeObserver(self.infiniteScrollingView!, forKeyPath: "contentOffset") self.infiniteScrollingView?.hidden = true } } } public func addInfiniteScrollingWithActionHandler(customer:UIView? = nil,actionHandler:Void -> ()){ if self.infiniteScrollingView == nil { let view = EZInfiniteScrollingView(frame: CGRectZero) view.infiniteScrollingHandler = actionHandler view.originalBottomInset = self.contentInset.bottom view.hidden = true self.infiniteScrollingView = view; self.addObserver(self.infiniteScrollingView!, forKeyPath: "contentSize", options:NSKeyValueObservingOptions.New, context: nil) self.addSubview(self.infiniteScrollingView!) } if customer == nil { self.infiniteScrollingView?.setCustomView(PullFooter(scrollView: self)) }else{ self.infiniteScrollingView?.setCustomView(customer!) } } public func triggerInfiniteScrolling(){ self.infiniteScrollingView?.oldState = .Triggered self.infiniteScrollingView?.startAnimating() } } ================================================ FILE: Pod/Classes/Easy/Lib/PullRefresh/EZPullToRefresh.swift ================================================ // // EZPullToRefresh.swift // Demo // // Created by zhuchao on 15/5/15. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond let EZPullToRefreshViewHeight:CGFloat = 60.0 public enum EZPullToRefreshState { case Stopped case Triggered case Loading case Pulling } public class Header : UIView { init(scrollView:UIScrollView,frame: CGRect = CGRectZero) { super.init(frame: frame) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func resetScrollViewContentInset(scrollView:UIScrollView){ var currentInsets = scrollView.contentInset currentInsets.top = scrollView.pullToRefreshView!.originalTopInset self.setScrollViewContentInset(currentInsets, scrollView: scrollView) } public func setScrollViewContentInsetForLoading(scrollView:UIScrollView){ let offset = max(EZPullToRefreshViewHeight, 0) var currentInsets = scrollView.contentInset currentInsets.top = max(offset, scrollView.pullToRefreshView!.originalTopInset + scrollView.pullToRefreshView!.bounds.size.height) self.setScrollViewContentInset(currentInsets, scrollView: scrollView) } public func setScrollViewContentInset(contentInset:UIEdgeInsets,scrollView:UIScrollView){ UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { scrollView.contentInset = contentInset },completion:nil) } } public class EZPullToRefreshView : UIView { public var state = Observable(.Stopped) public var originalTopInset:CGFloat = 0.0 public var originalBottomInset:CGFloat = 0.0 public var originalOffset:CGFloat = 0.0 private var pullToRefreshActionHandler:(Void -> ())? private var oldState = EZPullToRefreshState.Stopped private func commonInit(){ self.autoresizingMask = UIViewAutoresizing.FlexibleWidth self.state.observe{ [unowned self] state in if state == .Loading && self.oldState == .Triggered { self.pullToRefreshActionHandler?() } self.oldState = state } } override init(frame: CGRect) { super.init(frame: frame) self.commonInit() } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } func setCustomView(customView:UIView){ if customView.isKindOfClass(UIView) { for view in self.subviews { view.removeFromSuperview() } self.addSubview(customView) let viewBounds = customView.bounds; let origin = CGPointMake( CGFloat(roundf(Float(self.bounds.size.width-viewBounds.size.width)/2)), CGFloat(roundf(Float(self.bounds.size.height-viewBounds.size.height)/2))) customView.frame = CGRectMake(origin.x, origin.y, viewBounds.size.width, viewBounds.size.height) } } public func startAnimating(){ self.state.value = .Loading } public func stopAnimating(){ self.state.value = .Stopped } public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { let scrollView = object as! UIScrollView if keyPath == "contentOffset" && scrollView.showsPullToRefresh { if self.state.value != .Loading{ let pullNum = scrollView.contentOffset.y + self.originalTopInset if !scrollView.dragging && self.state.value == .Triggered { self.state.value = .Loading }else if scrollView.dragging && self.state.value == .Pulling && pullNum < -EZPullToRefreshViewHeight { self.state.value = .Triggered }else if pullNum <= -1 && pullNum > -EZPullToRefreshViewHeight { self.state.value = .Pulling }else if pullNum > -1 { self.state.value = .Stopped } } } } } private var PullToRefreshViewHandle :UInt8 = 0 extension UIScrollView { public var pullToRefreshView : EZPullToRefreshView? { get{ if let d: AnyObject = objc_getAssociatedObject(self, &PullToRefreshViewHandle) { return d as? EZPullToRefreshView }else{ return nil } }set (value){ objc_setAssociatedObject(self, &PullToRefreshViewHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var showsPullToRefresh :Bool { get{ return !self.pullToRefreshView!.hidden }set(value){ if value { self.addObserver(self.pullToRefreshView!, forKeyPath: "contentOffset", options:NSKeyValueObservingOptions.New, context: nil) self.pullToRefreshView?.hidden = false }else{ self.removeObserver(self.pullToRefreshView!, forKeyPath: "contentOffset") self.pullToRefreshView?.hidden = true } } } public func addPullToRefreshWithActionHandler(customer:UIView? = nil,actionHandler:Void -> ()){ if self.pullToRefreshView == nil { let view = EZPullToRefreshView(frame: CGRectZero) view.pullToRefreshActionHandler = actionHandler view.originalTopInset = self.contentInset.top; view.originalBottomInset = self.contentInset.bottom; self.pullToRefreshView = view; self.showsPullToRefresh = true; self.addSubview(self.pullToRefreshView!) } if customer == nil { self.pullToRefreshView?.setCustomView(PullHeader(scrollView: self)) }else{ self.pullToRefreshView?.setCustomView(customer!) } } public func triggerPullToRefresh(){ self.pullToRefreshView?.oldState = .Triggered self.pullToRefreshView?.startAnimating() } } ================================================ FILE: Pod/Classes/Easy/Lib/PullRefresh/PullFooter.swift ================================================ // // PullFooter.swift // Demo // // Created by zhuchao on 15/5/16. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond public class PullFooter : Footer { var arrowImage:UIImageView! var activityView:UIActivityIndicatorView! var statusLabel:UILabel! init (scrollView:UIScrollView){ super.init( scrollView: scrollView, frame: CGRectMake(0, 0, scrollView.superview!.size.width, EZInfiniteScrollingViewHeight)) self.commonInit() scrollView.infiniteScrollingView!.frame = CGRectMake(0,scrollView.contentSize.height, scrollView.superview!.size.width, EZInfiniteScrollingViewHeight) scrollView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.New, context: nil) scrollView.infiniteScrollingView!.state.observe{state in switch (state){ case .Ended: self.arrowImage.hidden = true self.activityView.stopAnimating() self.statusLabel.text = "没有了哦" case .Stopped: UIView.animateWithDuration(0.25){ [unowned self] in self.resetScrollViewContentInset(scrollView) self.arrowImage.hidden = false self.activityView.stopAnimating() self.statusLabel.text = "上拉加载" self.arrowImage.transform = CGAffineTransformMakeRotation(CGFloat(M_PI)) } case .Pulling: UIView.animateWithDuration(0.25){ [unowned self] in self.arrowImage.hidden = false self.activityView.stopAnimating(); self.statusLabel.text = "上拉加载" self.arrowImage.transform = CGAffineTransformMakeRotation(CGFloat(M_PI)) } case .Triggered: UIView.animateWithDuration(0.25){ [unowned self] in self.arrowImage.hidden = false; self.activityView.stopAnimating(); self.statusLabel.text = "释放加载" self.arrowImage.transform = CGAffineTransformIdentity } case .Loading: UIView.animateWithDuration(0.25){ [unowned self] in self.setScrollViewContentInsetForLoading(scrollView) self.arrowImage.hidden = true; self.activityView.startAnimating(); self.statusLabel.text = "正在加载..." } } } } public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { let scrollView = object as! UIScrollView if keyPath == "contentSize" && scrollView.contentSize.height > scrollView.bounds.size.height && scrollView.bounds.size.height > 0 { scrollView.infiniteScrollingView!.frame = CGRectMake(0, scrollView.contentSize.height, scrollView.superview!.size.width,EZInfiniteScrollingViewHeight) } } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } func commonInit(){ let bundle = NSBundle(forClass: self.dynamicType) let url = bundle.URLForResource("EasyIOS-Swift", withExtension: "bundle") let imageBundle = NSBundle(URL: url!) if imageBundle?.loaded == false { imageBundle?.load() } let arrow = UIImage(contentsOfFile: imageBundle!.pathForResource("arrow-down@2x", ofType: "png")!) arrowImage = UIImageView(image: arrow) arrowImage.autoresizingMask = [UIViewAutoresizing.FlexibleLeftMargin, UIViewAutoresizing.FlexibleRightMargin] self.addSubview(arrowImage) activityView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray) activityView.autoresizingMask = arrowImage.autoresizingMask; activityView.hidden = true self.addSubview(activityView) statusLabel = UILabel() statusLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth; statusLabel.font = UIFont.boldSystemFontOfSize(13); statusLabel.textColor = UIColor.blackColor(); statusLabel.backgroundColor = UIColor.clearColor(); statusLabel.textAlignment = NSTextAlignment.Center; self.addSubview(statusLabel) arrowImage.snp_makeConstraints(){[unowned self] (make) -> Void in make.centerY.equalTo(self.arrowImage.superview!) } activityView.snp_makeConstraints(){[unowned self] (make) -> Void in make.edges.equalTo(self.arrowImage) } statusLabel.snp_makeConstraints(){[unowned self] (make) -> Void in make.leading.equalTo(self.arrowImage).offset(30.0) make.center.equalTo(self.statusLabel.superview!) } } } ================================================ FILE: Pod/Classes/Easy/Lib/PullRefresh/PullHeader.swift ================================================ // // PullHeader.swift // Demo // // Created by zhuchao on 15/5/16. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import SnapKit import Bond public class PullHeader : Header { var arrowImage:UIImageView! var activityView:UIActivityIndicatorView! var statusLabel:UILabel! var lastUpdateTimeLabel:UILabel! init (scrollView:UIScrollView){ super.init( scrollView: scrollView, frame: CGRectMake(0, 0, scrollView.size.width, EZPullToRefreshViewHeight)) self.commonInit() scrollView.pullToRefreshView!.frame = CGRectMake(0, -EZPullToRefreshViewHeight, scrollView.superview!.size.width, EZPullToRefreshViewHeight) scrollView.pullToRefreshView!.state.observe{state in switch (state){ case .Stopped: UIView.animateWithDuration(0.25){ [unowned self] in self.resetScrollViewContentInset(scrollView) self.arrowImage.hidden = false self.activityView.stopAnimating() self.statusLabel.text = "下拉可以刷新" self.arrowImage.transform = CGAffineTransformIdentity self.updateTimeLabel(NSDate()) } case .Pulling: UIView.animateWithDuration(0.25){ [unowned self] in self.arrowImage.hidden = false self.activityView.stopAnimating(); self.statusLabel.text = "下拉可以刷新" self.arrowImage.transform = CGAffineTransformIdentity; } case .Triggered: UIView.animateWithDuration(0.25){ [unowned self] in self.arrowImage.hidden = false; self.activityView.stopAnimating(); self.statusLabel.text = "释放可以刷新" self.arrowImage.transform = CGAffineTransformMakeRotation(CGFloat(M_PI)); } case .Loading: UIView.animateWithDuration(0.25){ [unowned self] in self.setScrollViewContentInsetForLoading(scrollView) self.arrowImage.hidden = true; self.activityView.startAnimating(); self.statusLabel.text = "正在刷新..." } } } } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } func commonInit(){ let bundle = NSBundle(forClass: self.dynamicType) let url = bundle.URLForResource("EasyIOS-Swift", withExtension: "bundle") let imageBundle = NSBundle(URL: url!) if imageBundle?.loaded == false { imageBundle?.load() } let arrow = UIImage(contentsOfFile: imageBundle!.pathForResource("arrow-down@2x", ofType: "png")!) arrowImage = UIImageView(image: arrow) arrowImage.autoresizingMask = [UIViewAutoresizing.FlexibleLeftMargin, UIViewAutoresizing.FlexibleRightMargin] self.addSubview(arrowImage) activityView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray) activityView.autoresizingMask = arrowImage.autoresizingMask; activityView.hidden = true self.addSubview(activityView) statusLabel = UILabel() statusLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth; statusLabel.font = UIFont.boldSystemFontOfSize(13); statusLabel.textColor = UIColor.blackColor(); statusLabel.backgroundColor = UIColor.clearColor(); statusLabel.textAlignment = NSTextAlignment.Center; self.addSubview(statusLabel) lastUpdateTimeLabel = UILabel() lastUpdateTimeLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth; lastUpdateTimeLabel.font = UIFont.boldSystemFontOfSize(12); lastUpdateTimeLabel.textColor = UIColor.blackColor(); lastUpdateTimeLabel.backgroundColor = UIColor.clearColor(); lastUpdateTimeLabel.textAlignment = NSTextAlignment.Center; self.updateTimeLabel(NSDate()) self.addSubview(lastUpdateTimeLabel) arrowImage.snp_makeConstraints(){[unowned self] (make) -> Void in make.centerY.equalTo(self.arrowImage.superview!) } activityView.snp_makeConstraints(){[unowned self] (make) -> Void in make.edges.equalTo(self.arrowImage) } statusLabel.snp_makeConstraints(){[unowned self] (make) -> Void in make.top.equalTo(self.statusLabel.superview!).offset(10.0) make.centerX.equalTo(self.statusLabel.superview!) } lastUpdateTimeLabel.snp_makeConstraints(){[unowned self] (make) -> Void in make.bottom.equalTo(self.lastUpdateTimeLabel.superview!).offset(-15.0) make.centerX.equalTo(self.lastUpdateTimeLabel.superview!) make.leading.equalTo(self.arrowImage).offset(40) } } func updateTimeLabel(date:NSDate?){ if let aDate = date { // 1.获得年月日 let calendar = NSCalendar.currentCalendar() let unitFlags: NSCalendarUnit = [NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute] let cmp1 = calendar.components(unitFlags, fromDate: aDate) let now = calendar.components(unitFlags, fromDate: NSDate()) // 2.格式化日期 let formatter = NSDateFormatter() if (cmp1.day == now.day) { // 今天 formatter.dateFormat = "今天 HH:mm"; } else if (cmp1.year == now.year) { // 今年 formatter.dateFormat = "MM-dd HH:mm"; } else { formatter.dateFormat = "yyyy-MM-dd HH:mm"; } // 3.显示日期 self.lastUpdateTimeLabel.text = "最后更新:" + formatter.stringFromDate(aDate) } } } ================================================ FILE: Pod/Classes/Extend/EUI/Defines.h ================================================ // // GTMDefines.h // // Copyright 2008 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. // // ============================================================================ #include // Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs #ifndef MAC_OS_X_VERSION_10_5 #define MAC_OS_X_VERSION_10_5 1050 #endif #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif // ---------------------------------------------------------------------------- // CPP symbols that can be overridden in a prefix to control how the toolbox // is compiled. // ---------------------------------------------------------------------------- // GTMHTTPFetcher will support logging by default but only hook its input // stream support for logging when requested. You can control the inclusion of // the code by providing your own definitions for these w/in a prefix header. // #ifndef GTM_HTTPFETCHER_ENABLE_LOGGING #define GTM_HTTPFETCHER_ENABLE_LOGGING 1 #endif // GTM_HTTPFETCHER_ENABLE_LOGGING #ifndef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING #define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0 #endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING // By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and // GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens // when a validation fails. If you implement your own validators, you may want // to control their internals using the same macros for consistency. #ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 #endif // Give ourselves a consistent way to do inlines. Apple's macros even use // a few different actual definitions, so we're based off of the foundation // one. #if !defined(GTM_INLINE) #if defined (__GNUC__) && (__GNUC__ == 4) #define GTM_INLINE static __inline__ __attribute__((always_inline)) #else #define GTM_INLINE static __inline__ #endif #endif // Give ourselves a consistent way of doing externs that links up nicely // when mixing objc and objc++ #if !defined (GTM_EXTERN) #if defined __cplusplus #define GTM_EXTERN extern "C" #else #define GTM_EXTERN extern #endif #endif // Give ourselves a consistent way of exporting things if we have visibility // set to hidden. #if !defined (GTM_EXPORT) #define GTM_EXPORT __attribute__((visibility("default"))) #endif // _GTMDevLog & _GTMDevAssert // // _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for // developer level errors. This implementation simply macros to NSLog/NSAssert. // It is not intended to be a general logging/reporting system. // // Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert // for a little more background on the usage of these macros. // // _GTMDevLog log some error/problem in debug builds // _GTMDevAssert assert if conditon isn't met w/in a method/function // in all builds. // // To replace this system, just provide different macro definitions in your // prefix header. Remember, any implementation you provide *must* be thread // safe since this could be called by anything in what ever situtation it has // been placed in. // // We only define the simple macros if nothing else has defined this. #ifndef _GTMDevLog #ifdef DEBUG #define _GTMDevLog(...) NSLog(__VA_ARGS__) #else #define _GTMDevLog(...) do { } while (0) #endif #endif // _GTMDevLog // Declared here so that it can easily be used for logging tracking if // necessary. See GTMUnitTestDevLog.h for details. @class NSString; GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); #ifndef _GTMDevAssert // we directly invoke the NSAssert handler so we can pass on the varargs // (NSAssert doesn't have a macro we can use that takes varargs) #if !defined(NS_BLOCK_ASSERTIONS) #define _GTMDevAssert(condition, ...) \ do { \ if (!(condition)) { \ [[NSAssertionHandler currentHandler] \ handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ file:[NSString stringWithUTF8String:__FILE__] \ lineNumber:__LINE__ \ description:__VA_ARGS__]; \ } \ } while(0) #else // !defined(NS_BLOCK_ASSERTIONS) #define _GTMDevAssert(condition, ...) do { } while (0) #endif // !defined(NS_BLOCK_ASSERTIONS) #endif // _GTMDevAssert // _GTMCompileAssert // _GTMCompileAssert is an assert that is meant to fire at compile time if you // want to check things at compile instead of runtime. For example if you // want to check that a wchar is 4 bytes instead of 2 you would use // _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) // Note that the second "arg" is not in quotes, and must be a valid processor // symbol in it's own right (no spaces, punctuation etc). // Wrapping this in an #ifndef allows external groups to define their own // compile time assert scheme. #ifndef _GTMCompileAssert // We got this technique from here: // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) #define _GTMCompileAssert(test, msg) \ typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] #endif // _GTMCompileAssert // Macro to allow fast enumeration when building for 10.5 or later, and // reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration // does keys, so pick the right thing, nothing is done on the FastEnumeration // side to be sure you're getting what you wanted. #ifndef GTM_FOREACH_OBJECT #if defined(TARGET_OS_IPHONE) || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) #define GTM_FOREACH_OBJECT(element, collection) \ for (element in collection) #define GTM_FOREACH_KEY(element, collection) \ for (element in collection) #else #define GTM_FOREACH_OBJECT(element, collection) \ for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \ (element = [_ ## element ## _enum nextObject]) != nil; ) #define GTM_FOREACH_KEY(element, collection) \ for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \ (element = [_ ## element ## _enum nextObject]) != nil; ) #endif #endif // ============================================================================ // ---------------------------------------------------------------------------- // CPP symbols defined based on the project settings so the GTM code has // simple things to test against w/o scattering the knowledge of project // setting through all the code. // ---------------------------------------------------------------------------- // Provide a single constant CPP symbol that all of GTM uses for ifdefing // iPhone code. #include #if TARGET_OS_IPHONE // iPhone SDK // For iPhone specific stuff #define GTM_IPHONE_SDK 1 #if TARGET_IPHONE_SIMULATOR #define GTM_IPHONE_SIMULATOR 1 #else #define GTM_IPHONE_DEVICE 1 #endif // TARGET_IPHONE_SIMULATOR #else // For MacOS specific stuff #define GTM_MACOS_SDK 1 #endif // Provide a symbol to include/exclude extra code for GC support. (This mainly // just controls the inclusion of finalize methods). #ifndef GTM_SUPPORT_GC #if GTM_IPHONE_SDK // iPhone never needs GC #define GTM_SUPPORT_GC 0 #else // We can't find a symbol to tell if GC is supported/required, so best we // do on Mac targets is include it if we're on 10.5 or later. #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 #define GTM_SUPPORT_GC 0 #else #define GTM_SUPPORT_GC 1 #endif #endif #endif // To simplify support for 64bit (and Leopard in general), we provide the type // defines for non Leopard SDKs #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 // NSInteger/NSUInteger and Max/Mins #ifndef NSINTEGER_DEFINED #if __LP64__ || NS_BUILD_32_LIKE_64 typedef long NSInteger; typedef unsigned long NSUInteger; #else typedef int NSInteger; typedef unsigned int NSUInteger; #endif #define NSIntegerMax LONG_MAX #define NSIntegerMin LONG_MIN #define NSUIntegerMax ULONG_MAX #define NSINTEGER_DEFINED 1 #endif // NSINTEGER_DEFINED // CGFloat #ifndef CGFLOAT_DEFINED #if defined(__LP64__) && __LP64__ // This really is an untested path (64bit on Tiger?) typedef double CGFloat; #define CGFLOAT_MIN DBL_MIN #define CGFLOAT_MAX DBL_MAX #define CGFLOAT_IS_DOUBLE 1 #else /* !defined(__LP64__) || !__LP64__ */ typedef float CGFloat; #define CGFLOAT_MIN FLT_MIN #define CGFLOAT_MAX FLT_MAX #define CGFLOAT_IS_DOUBLE 0 #endif /* !defined(__LP64__) || !__LP64__ */ #define CGFLOAT_DEFINED 1 #endif // CGFLOAT_DEFINED #endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 ================================================ FILE: Pod/Classes/Extend/EUI/DesEncrypt.h ================================================ // // DesEncrypt.h // medical // // Created by zhuchao on 15/5/4. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import @interface DesEncrypt : NSObject + (NSString *)encryptWithText:(NSString *)sText key:(NSString *)key;//加密 + (NSString *)decryptWithText:(NSString *)sText key:(NSString *)key;//解密 @end ================================================ FILE: Pod/Classes/Extend/EUI/DesEncrypt.m ================================================ // // DesEncrypt.m // medical // // Created by zhuchao on 15/5/4. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import "DesEncrypt.h" #import @implementation DesEncrypt + (NSString *)encryptWithText:(NSString *)sText key:(NSString *)key { //kCCEncrypt 加密 return [self encrypt:sText encryptOrDecrypt:kCCEncrypt key:[NSString stringWithFormat:@"%@",key]]; } + (NSString *)decryptWithText:(NSString *)sText key:(NSString *)key { //kCCDecrypt 解密 return [self encrypt:sText encryptOrDecrypt:kCCDecrypt key:[NSString stringWithFormat:@"%@",key]]; } + (NSString *)encrypt:(NSString *)sText encryptOrDecrypt:(CCOperation)encryptOperation key:(NSString *)key { const void *dataIn; size_t dataInLength; if (encryptOperation == kCCDecrypt)//传递过来的是decrypt 解码 { //解码 base64 // [[sText dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength] NSData *decryptData = [[NSData alloc]initWithBase64EncodedString:sText options:NSDataBase64DecodingIgnoreUnknownCharacters];//转成utf-8并decode dataInLength = [decryptData length]; dataIn = [decryptData bytes]; } else //encrypt { NSData* encryptData = [sText dataUsingEncoding:NSUTF8StringEncoding]; dataInLength = [encryptData length]; dataIn = (const void *)[encryptData bytes]; } /* DES加密 :用CCCrypt函数加密一下,然后用base64编码下,传过去 DES解密 :把收到的数据根据base64,decode一下,然后再用CCCrypt函数解密,得到原本的数据 */ CCCryptorStatus ccStatus; uint8_t *dataOut = NULL; //可以理解位type/typedef 的缩写(有效的维护了代码,比如:一个人用int,一个人用long。最好用typedef来定义) size_t dataOutAvailable = 0; //size_t 是操作符sizeof返回的结果类型 size_t dataOutMoved = 0; dataOutAvailable = (dataInLength + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1); dataOut = malloc( dataOutAvailable * sizeof(uint8_t)); memset((void *)dataOut, 0x0, dataOutAvailable);//将已开辟内存空间buffer的首 1 个字节的值设为值 0 NSString *initIv = @"12345678"; const void *vkey = (const void *) [key UTF8String]; const void *iv = (const void *) [initIv UTF8String]; //CCCrypt函数 加密/解密 ccStatus = CCCrypt(encryptOperation,// 加密/解密 kCCAlgorithmDES,// 加密根据哪个标准(des,3des,aes。。。。) kCCOptionPKCS7Padding,// 选项分组密码算法(des:对每块分组加一次密 3DES:对每块分组加三个不同的密) vkey, //密钥 加密和解密的密钥必须一致 kCCKeySizeDES,// DES 密钥的大小(kCCKeySizeDES=8) iv, // 可选的初始矢量 dataIn, // 数据的存储单元 dataInLength,// 数据的大小 (void *)dataOut,// 用于返回数据 dataOutAvailable, &dataOutMoved); NSString *result = nil; if (encryptOperation == kCCDecrypt)//encryptOperation==1 解码 { //得到解密出来的data数据,改变为utf-8的字符串 result = [[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)dataOut length:(NSUInteger)dataOutMoved] encoding:NSUTF8StringEncoding]; } else //encryptOperation==0 (加密过程中,把加好密的数据转成base64的) { //编码 base64 NSData *data = [NSData dataWithBytes:(const void *)dataOut length:(NSUInteger)dataOutMoved]; result = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; } return result; } @end ================================================ FILE: Pod/Classes/Extend/EUI/ENSObject.swift ================================================ // // ENSObject.swift // Pods // // Created by zhuchao on 15/7/21. // // import Foundation import JavaScriptCore @objc public protocol ENSObject:JSExport{ func val(keyPath:String) -> AnyObject? func attr(keyPath:String,_ value:AnyObject?) func attrs(dict:[NSObject : AnyObject]!) func call(selector:String) func call(selector:String,withObject object:AnyObject?) } public extension NSObject{ public func call(selector:String){ NSThread.detachNewThreadSelector(Selector(selector), toTarget:self, withObject: nil) } public func call(selector:String,withObject object:AnyObject?){ NSThread.detachNewThreadSelector(Selector(selector), toTarget:self, withObject: object) } public func attr(key:String,_ value:AnyObject?) { SwiftTryCatch.`try`({ if let str = value as? String { self.setValue(str.anyValue(key.toKeyPath), forKeyPath: key.toKeyPath) }else{ self.setValue(value, forKeyPath: key.toKeyPath) } }, `catch`: { (error) in print("JS Error:\(error.description)") }, finally: nil) } public func attrs(var dict:[String : AnyObject]!){ SwiftTryCatch.`try`({ for (key, value) in dict { if let str = value as? String { dict[key.toKeyPath] = str.anyValue(key.toKeyPath) } } self.setValuesForKeysWithDictionary(dict) }, `catch`: { (error) in print("JS Error:\(error.description)") }, finally: nil) } public func val(key:String) -> AnyObject? { var result:AnyObject? SwiftTryCatch.`try`({ result = self.valueForKeyPath(key.toKeyPath) }, `catch`: { (error) in print("JS Error:\(error.description)") }, finally: nil) return result } } @objc public protocol EZActionJSExport:JSExport{ static func SEND_IQ_CACHE (req:EZRequest) static func SEND_CACHE (req:EZRequest) static func SEND (req:EZRequest) static func Upload (req:EZRequest) static func Download (req:EZRequest) } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+ButtonProperty.swift ================================================ // // EUI+ButtonProperty.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation class ButtonProperty:ViewProperty{ var highlightedStyle = "" var disabledStyle = "" var selectedStyle = "" var applicationStyle = "" var reservedStyle = "" var highlightedText:String? var disabledText:String? var selectedText:String? var applicationText:String? var reservedText:String? var onEvent:SelectorAction? override func view() -> UIButton{ let view = UIButton() view.tagProperty = self if self.style != "" { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.contentText?.toData(), attributes: ["html":self.style]), forState: UIControlState.Normal) } if self.highlightedText != nil { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.highlightedText?.toData(), attributes: ["html":self.highlightedStyle]), forState: UIControlState.Highlighted) } if self.disabledText != nil { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.disabledText?.toData(), attributes: ["html":self.disabledStyle]), forState: UIControlState.Disabled) } if self.selectedText != nil { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.selectedText?.toData(), attributes: ["html":self.selectedStyle]), forState: UIControlState.Selected) } if self.applicationText != nil { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.applicationText?.toData(), attributes: ["html":self.applicationStyle]), forState: UIControlState.Application) } if self.reservedText != nil { view.setAttributedTitle(NSAttributedString(fromHTMLData: self.reservedText?.toData(), attributes: ["html":self.reservedStyle]), forState: UIControlState.Reserved) } self.renderViewStyle(view) return view } override func renderTag(pelement: OGElement) { self.tagOut += ["highlighted","disabled","selected","application","reserved","disabled-text", "selected-text","application-text","reserved-text","highlighted-text","onevent"] super.renderTag(pelement) if let highlightedStyle = EUIParse.string(pelement,key: "highlighted") { self.highlightedStyle = "html{" + highlightedStyle + "}" } if let disabledStyle = EUIParse.string(pelement,key: "disabled") { self.disabledStyle = "html{" + disabledStyle + "}" } if let selectedStyle = EUIParse.string(pelement,key: "selected") { self.selectedStyle = "html{" + selectedStyle + "}" } if let applicationStyle = EUIParse.string(pelement,key: "application") { self.applicationStyle = "html{" + applicationStyle + "}" } if let reservedStyle = EUIParse.string(pelement,key: "reserved") { self.reservedStyle = "html{" + reservedStyle + "}" } self.disabledText = EUIParse.string(pelement,key: "disabled-text") self.selectedText = EUIParse.string(pelement,key: "selected-text") self.applicationText = EUIParse.string(pelement,key: "application-text") self.reservedText = EUIParse.string(pelement,key: "reserved-text") self.highlightedText = EUIParse.string(pelement,key: "highlighted-text") if let theSelector = EUIParse.string(pelement, key: "onevent") { var values = theSelector.trimArrayBy(":") if values.count == 2 { self.onEvent = SelectorAction(selector: values[1], event: values[0]) } } var html = "" for child in pelement.children { html += child.html().trim } if let newHtml = self.bindTheKeyPath(html, key: "text") { html = newHtml } self.contentText = html } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+CollectionViewProperty.swift ================================================ // // EUI+CollectionViewProperty.swift // medical // // Created by zhuchao on 15/5/10. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import Bond class CollectionViewProperty: ScrollViewProperty { var separatorInset:UIEdgeInsets? var reuseCell = Dictionary() var flowLayout = Dictionary() var layout:String? override func view() -> UICollectionView{ let view = UICollectionView(frame: CGRectZero, collectionViewLayout: self.getLayout()) view.tagProperty = self self.renderViewStyle(view) for (reuseId,_) in self.reuseCell { view.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseId) } return view } override func renderTag(pelement:OGElement){ self.tagOut += ["delegate","datasource","flow-layout","layout"] super.renderTag(pelement) if let layout = EUIParse.string(pelement, key: "flow-layout") { let dict = layout.trimArrayBy(";") for value in dict { var array = value.trimArrayBy(":") let key = array[0] as String let val = array[1] as String self.flowLayout[key.toKeyPath] = val } } self.layout = EUIParse.string(pelement, key: "layout") } func getLayout() -> UICollectionViewLayout{ if let customlayout = self.layout,let nsobject = NSObject(fromString: customlayout) as? UICollectionViewLayout { return nsobject } let layout = UICollectionViewFlowLayout() for (key,value) in self.flowLayout{ if key == "minimumLineSpacing" || key == "minimumInteritemSpacing"{ layout.setValue(value.floatValue, forKeyPath: key) }else if key == "itemSize" { layout.itemSize = CGSizeFromString(value) }else if key == "estimatedItemSize" { layout.estimatedItemSize = CGSizeFromString(value) }else if key == "headerReferenceSize" { layout.headerReferenceSize = CGSizeFromString(value) }else if key == "footerReferenceSize"{ layout.footerReferenceSize = CGSizeFromString(value) }else if key == "sectionInset" { layout.sectionInset = UIEdgeInsetsFromString(value) }else if key == "scrollDirection" { if value.lowercaseString == "Vertical".lowercaseString { layout.scrollDirection = .Vertical }else if value.lowercaseString == "Horizontal".lowercaseString { layout.scrollDirection = .Horizontal } } } return layout } override func childLoop(pelement: OGElement) { for element in pelement.children { if let ele = element as? OGElement, let type = EUIParse.string(ele, key: "type"), let tagId = EUIParse.string(ele, key: "id"), let property = EUIParse.loopElement(ele){ if type.lowercaseString == "cell" { self.reuseCell[tagId] = property } } } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+ImageProperty.swift ================================================ // // EUI+ImageProperty.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation class ImageProperty:ViewProperty{ var src = "" override func view() -> UIImageView{ let view = UIImageView() view.tagProperty = self if !self.src.characters.isEmpty { if self.src.hasPrefix("http") { view.kf_setImageWithURL(NSURL(string: self.src)!) }else{ view.image = UIImage(named: self.src) } } self.renderViewStyle(view) for subTag in self.subTags { view.addSubview(subTag.getView()) } return view } override func renderTag(pelement:OGElement){ self.tagOut += ["src"] super.renderTag(pelement) if let src = EUIParse.string(pelement,key:"src"),let filterHtml = self.bindTheKeyPath(src, key: "src") { self.src = filterHtml } } override func childLoop(pelement: OGElement) { } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+LabelProperty.swift ================================================ // // EUI+LabelProperty.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import JavaScriptCore import TTTAttributedLabel class LabelProperty:ViewProperty{ var linkStyle = Dictionary() var activeLinkStyle = Dictionary() var textAlignment:NSTextAlignment = .Left override func view() -> UIView{ if self.style.characters.isEmpty { let view = UILabel() view.tagProperty = self view.text = self.contentText self.renderViewStyle(view) return view }else{ let view = TTTAttributedLabel(frame: CGRectZero) view.tagProperty = self if self.linkStyle.count > 0 { view.linkAttributes = self.linkStyle } if self.activeLinkStyle.count > 0 { view.activeLinkAttributes = self.activeLinkStyle } view.setText(NSAttributedString(fromHTMLData: self.contentText?.toData(), attributes: ["dict":self.style])) self.renderViewStyle(view) return view } } override func renderTag(pelement: OGElement) { self.tagOut += ["link-style","active-link-style","text-alignment"] super.renderTag(pelement) if let textAlignment = EUIParse.string(pelement,key:"text-alignment") { self.textAlignment = textAlignment.textAlignment } if let linkStyle = EUIParse.string(pelement,key:"link-style") { self.linkStyle = linkStyle.linkStyleDict } if let linkStyle = EUIParse.string(pelement,key:"active-link-style") { self.activeLinkStyle = linkStyle.linkStyleDict } var html = "" for child in pelement.children { html += child.html().trim } var bindKey = "text" if !self.style.characters.isEmpty { bindKey = "TTText" } if let newHtml = self.bindTheKeyPath(html, key: bindKey) { html = newHtml } self.contentText = html } override func childLoop(pelement: OGElement) { } override func renderViewStyle(view:UIView){ super.renderViewStyle(view) let sview = view as! UILabel sview.textAlignment = self.textAlignment } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+ScrollViewProperty.swift ================================================ // // EUI+ScrollViewProperty.swift // medical // // Created by zhuchao on 15/5/2. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation class ScrollViewProperty:ViewProperty{ var contentInset = UIEdgeInsetsZero var contentOffset = CGPointZero var contentSize = CGSizeZero var scrollIndicatorInsets = UIEdgeInsetsZero var indicatorStyle:UIScrollViewIndicatorStyle = .Default var pullToRefresh:PullRefreshAction? var infiniteScrolling:InfiniteScrollingAction? override func view() -> UIScrollView{ let view = UIScrollView() view.tagProperty = self self.renderViewStyle(view) for subTag in self.subTags { view.addSubview(subTag.getView()) } return view } override func renderViewStyle(view: UIView) { super.renderViewStyle(view) let sview = view as! UIScrollView sview.contentInset = self.contentInset sview.contentOffset = self.contentOffset sview.contentSize = self.contentSize sview.scrollIndicatorInsets = self.scrollIndicatorInsets sview.indicatorStyle = self.indicatorStyle } override func renderTag(pelement:OGElement){ self.tagOut += ["content-offset","content-inset","content-size","scroll-indicator-insets","indicator-style","pull-to-refresh","infinite-scrolling"] super.renderTag(pelement) if let contentInset = EUIParse.string(pelement,key:"content-inset") { self.contentInset = UIEdgeInsetsFromString(contentInset) } if let contentOffset = EUIParse.string(pelement,key:"content-offset") { self.contentOffset = CGPointFromString(contentOffset) } if let contentSize = EUIParse.string(pelement,key:"content-size") { self.contentSize = CGSizeFromString(contentSize) } if let indicatorStyle = EUIParse.string(pelement,key:"indicator-style") { self.indicatorStyle = indicatorStyle.scrollViewIndicatorStyle } if let scrollIndicatorInsets = EUIParse.string(pelement,key:"scroll-indicator-insets") { self.scrollIndicatorInsets = UIEdgeInsetsFromString(scrollIndicatorInsets) } if let thePullRefresh = EUIParse.string(pelement, key: "pull-to-refresh") { var values = thePullRefresh.trimArray if values.count == 1 { self.pullToRefresh = PullRefreshAction(selector: values[0]) }else if values.count == 2 { self.pullToRefresh = PullRefreshAction(selector: values[0], viewClass: values[1]) } } if let theInfiniteScrolling = EUIParse.string(pelement, key: "infinite-scrolling") { var values = theInfiniteScrolling.trimArray if values.count == 1 { self.infiniteScrolling = InfiniteScrollingAction(selector: values[0]) }else if values.count == 2 { self.infiniteScrolling = InfiniteScrollingAction(selector: values[0], viewClass: values[1]) } } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+TableViewProperty.swift ================================================ // // EUI+TableViewProperty.swift // medical // // Created by zhuchao on 15/5/2. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation import Bond class TableViewProperty:ScrollViewProperty{ var tableViewStyle = UITableViewStyle.Plain var separatorInset:UIEdgeInsets? var rowHeight = UITableViewAutomaticDimension var reuseCell = Dictionary() var sectionView = Dictionary() var separatorStyle = UITableViewCellSeparatorStyle.SingleLine override func view() -> UITableView{ let view = UITableView(frame: CGRectZero, style: self.tableViewStyle) view.tagProperty = self view.rowHeight = self.rowHeight view.separatorStyle = self.separatorStyle; if let inset = self.separatorInset { view.separatorInset = inset; } self.renderViewStyle(view) for (reuseId,_) in self.reuseCell { view.registerClass(UITableViewCell.self, forCellReuseIdentifier: reuseId) } return view } override func renderTag(pelement:OGElement){ self.tagOut += ["table-view-style","separator-inset","delegate","datasource","row-height","separator-style"] super.renderTag(pelement) if let style = EUIParse.string(pelement,key:"table-view-style") { self.tableViewStyle = style.tableViewStyle } if let rowHeight = EUIParse.string(pelement, key: "row-height") { self.rowHeight = rowHeight.floatValue } if let separatorInset = EUIParse.string(pelement,key:"separator-inset") { self.separatorInset = UIEdgeInsetsFromString(separatorInset) } if let separatorStyle = EUIParse.string(pelement, key: "separator-style") { self.separatorStyle = separatorStyle.separatorStyle } } override func childLoop(pelement: OGElement) { for element in pelement.children { if let ele = element as? OGElement, let type = EUIParse.string(ele, key: "type"), let tagId = EUIParse.string(ele, key: "id"), let property = EUIParse.loopElement(ele){ if type.lowercaseString == "cell" || type.lowercaseString == "UITableViewCell" { self.reuseCell[tagId] = property }else if type.lowercaseString == "section" || type.lowercaseString == "UITableViewSection"{ self.sectionView[tagId] = property } } } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+TextFieldProperty.swift ================================================ // // EUI+TextFieldProperty.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation class TextFieldProperty:ViewProperty{ var placeholder:NSData? var placeholderStyle = "" var text:NSData? var keyboardType = UIKeyboardType.Default override func view() -> UITextField{ let view = UITextField() view.tagProperty = self view.keyboardType = self.keyboardType let str = NSAttributedString(fromHTMLData:self.text, attributes: ["html":self.style]) view.defaultTextAttributes = str.attributesAtIndex(0, effectiveRange:nil) view.attributedPlaceholder = NSAttributedString(fromHTMLData: self.placeholder, attributes: ["html":self.placeholderStyle]) self.renderViewStyle(view) return view } override func renderTag(pelement: OGElement) { self.tagOut += ["placeholder","placeholder-style","text","keyboard-type"] super.renderTag(pelement) if let text = EUIParse.string(pelement, key: "text"), let newHtml = Regex("\\{\\{(\\w+)\\}\\}").replace(text, withBlock: { (regx) -> String in let keyPath = regx.subgroupMatchAtIndex(0)?.trim self.bind["text"] = keyPath return "" }) { self.contentText = newHtml } self.text = "1".dataUsingEncoding(NSUTF8StringEncoding)?.dataByReplacingOccurrencesOfData("\\n".dataUsingEncoding(NSUTF8StringEncoding), withData: "\n".dataUsingEncoding(NSUTF8StringEncoding)) if let placeholderStyle = EUIParse.string(pelement,key: "placeholder-style") { self.placeholderStyle = "html{" + placeholderStyle + "}" } if let placeholder = EUIParse.string(pelement,key: "placeholder") { self.placeholder = placeholder.dataUsingEncoding(NSUTF8StringEncoding)?.dataByReplacingOccurrencesOfData("\\n".dataUsingEncoding(NSUTF8StringEncoding), withData: "\n".dataUsingEncoding(NSUTF8StringEncoding)) } if let keyboardType = EUIParse.string(pelement, key: "keyboard-type") { self.keyboardType = keyboardType.keyboardType } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI+ViewProperty.swift ================================================ // // EUI+ViewProperty.swift // medical // // Created by zhuchao on 15/5/2. // Copyright (c) 2015年 zhuchao. All rights reserved. // import Foundation class ViewProperty :NSObject{ var tag:GumboTag? var tagId = "" var style = "" var type = "" var subTags = Array() var otherProperty = Dictionary() var tagOut = Array() var imageMode = UIViewContentMode.ScaleAspectFill var align = Array() var margin = Array() var width:Constrain? var height:Constrain? var onTap:TapGestureAction? var onSwipe:SwipeGestureAction? var onTapBind:TapGestureAction? var onSwipeBind:SwipeGestureAction? var frame:CGRect? var bind = Dictionary() var contentText:String? var flexEnable = false var flexDirection:FLEXBOXFlexDirection = .Column var flexContentDirection:FLEXBOXContentDirection = .LeftToRight var flexJustifyContent:FLEXBOXJustification = .FlexStart var flexAlignSelf:FLEXBOXAlignment = .Auto var flexAlignItems:FLEXBOXAlignment = .Stretch var flexMargin = UIEdgeInsetsZero var flexPadding = UIEdgeInsetsZero var flexWrap = false var flex:CGFloat = 0.0 var flexFixedSize = CGSizeZero var flexMinimumSize = CGSizeZero var flexMaximumSize = CGSizeMake(CGFloat.max, CGFloat.max) func getView() -> UIView{ if self.tag == nil { return UIView() } return self.view() } func view() -> UIView{ if(flexEnable){ let view = FLEXBOXContainerView() view.tagProperty = self self.renderViewStyle(view) for subTag in self.subTags { view.addSubview(subTag.getView()) } return view }else{ let view = UIView() view.tagProperty = self self.renderViewStyle(view) for subTag in self.subTags { view.addSubview(subTag.getView()) } return view } } func renderTag(pelement:OGElement){ self.tagOut += ["id","style","align","margin","type","image-mode","name","width","height","class","ontap","onswipe","ontap-bind","onswipe-bind","frame","reuseid","push","present","align-self","align-items","justify-content","flex-direction","content-direction","flex-margin","flex-padding","flex-wrap","flex","flex-minimum-size"] self.tag = pelement.tag if let flexMinimumSize = EUIParse.string(pelement, key: "flex-minimum-size"){ self.flexEnable = true self.flexMinimumSize = CGSizeFromString(flexMinimumSize) } if let flexMaximumSize = EUIParse.string(pelement, key: "flex-maximum-size"){ self.flexEnable = true self.flexMaximumSize = CGSizeFromString(flexMaximumSize) } if let flexFixedSize = EUIParse.string(pelement, key: "flex-fixed-size"){ self.flexEnable = true self.flexFixedSize = CGSizeFromString(flexFixedSize) } if let flexMargin = EUIParse.string(pelement, key: "flex-margin"){ self.flexEnable = true self.flexMargin = UIEdgeInsetsFromString(flexMargin) } if let flexPadding = EUIParse.string(pelement, key: "flex-padding"){ self.flexEnable = true self.flexPadding = UIEdgeInsetsFromString(flexPadding) } if let flexWrap = EUIParse.string(pelement, key: "flex-wrap"){ self.flexEnable = true self.flexWrap = flexWrap.boolValue } if let flex = EUIParse.string(pelement, key: "flex"){ self.flexEnable = true self.flex = flex.floatValue } if let alignSelf = EUIParse.string(pelement, key: "align-self"){ self.flexEnable = true self.flexAlignSelf = alignSelf.alignItems } if let alignItems = EUIParse.string(pelement, key: "align-items"){ self.flexEnable = true self.flexAlignItems = alignItems.alignItems } if let justifyContent = EUIParse.string(pelement, key: "justify-content"){ self.flexEnable = true self.flexJustifyContent = justifyContent.justifyContent } if let flexDirection = EUIParse.string(pelement, key: "flex-direction"){ self.flexEnable = true self.flexDirection = flexDirection.flexDirection } if let contentDirection = EUIParse.string(pelement, key: "content-direction"){ self.flexEnable = true self.flexContentDirection = contentDirection.flexContentDirection } if let tagId = EUIParse.string(pelement,key:"id") { self.tagId = tagId } if let style = EUIParse.string(pelement,key:"style") { self.style = "html{" + style + "}" } if let theAlign = EUIParse.getStyleProperty(pelement,key: "align") { self.align = theAlign } if let theMargin = EUIParse.getStyleProperty(pelement,key: "margin") { self.margin = theMargin } if let theWidth = EUIParse.string(pelement,key:"width") { var values = theWidth.trimArray if values.count == 1 { if values[0].hasSuffix("%") { var val = values[0] val.removeAtIndex(val.startIndex.advancedBy(val.characters.count - 1)) self.width = Constrain(name:.Width,value: CGFloat(val.floatValue/100)) }else{ self.width = Constrain(name:.Width,value: CGFloat(values[0].trim.floatValue),target:"") } }else if values.count >= 2 && values[0].trim.hasSuffix("%"){ var val = values[0].trim val.removeAtIndex(val.startIndex.advancedBy(val.characters.count - 1)) self.width = Constrain(name:.Width,value: CGFloat(val.floatValue/100),target:values[1].trim) } } if let theHeight = EUIParse.string(pelement,key:"height") { var values = theHeight.trimArray if values.count == 1 { if values[0].hasSuffix("%") { var val = values[0] val.removeAtIndex(val.startIndex.advancedBy(val.characters.count - 1)) self.height = Constrain(name:.Height,value: CGFloat(val.floatValue/100)) }else{ self.height = Constrain(name:.Height,value: CGFloat(values[0].trim.floatValue),target:"") } }else if values.count >= 2 && values[0].trim.hasSuffix("%"){ var val = values[0].trim val.removeAtIndex(val.startIndex.advancedBy(val.characters.count - 1)) self.height = Constrain(name:.Height,value: CGFloat(val.floatValue/100),target:values[1].trim) } } if let frame = EUIParse.string(pelement, key: "frame") { self.frame = CGRectFromString(frame) } if let theType = EUIParse.string(pelement,key:"type") { self.type = theType } if let theImageMode = EUIParse.string(pelement,key:"image-mode") { self.imageMode = theImageMode.viewContentMode } if let theGestureAction = EUIParse.string(pelement, key: "ontap") { var values = theGestureAction.trimArray if values.count == 1 { self.onTap = TapGestureAction(selector: values[0]) }else if values.count >= 2 { self.onTap = TapGestureAction(selector: values[0], tapNumber: values[1]) } } if let theGestureAction = EUIParse.string(pelement, key: "onswipe") { var values = theGestureAction.trimArray if values.count == 2 { self.onSwipe = SwipeGestureAction(selector: values[0], direction: values[1]) }else if values.count >= 3 { self.onSwipe = SwipeGestureAction(selector: values[0], direction: values[1], numberOfTouches: values[2]) } } if let theGestureAction = EUIParse.string(pelement, key: "ontap-bind") { var values = theGestureAction.trimArray if values.count == 1 { self.onTapBind = TapGestureAction(selector: values[0]) }else if values.count >= 2 { self.onTapBind = TapGestureAction(selector: values[0], tapNumber: values[1]) } } if let theGestureAction = EUIParse.string(pelement, key: "onswipe-bind") { var values = theGestureAction.trimArray if values.count == 2 { self.onSwipeBind = SwipeGestureAction(selector: values[0], direction: values[1]) }else if values.count >= 3 { self.onSwipeBind = SwipeGestureAction(selector: values[0], direction: values[1], numberOfTouches: values[2]) } } for (key,value) in pelement.attributes { if self.tagOut.contains((key as! String)) == false { self.otherProperty[key as! String] = value } } self.childLoop(pelement) } func renderViewStyle(view:UIView){ view.contentMode = self.imageMode; if(self.flexEnable){ view.flexDirection = self.flexDirection view.flexContentDirection = self.flexContentDirection view.flexAlignItems = self.flexAlignItems view.flexAlignSelf = self.flexAlignSelf view.flexJustifyContent = self.flexJustifyContent view.flexMargin = self.flexMargin view.flexPadding = self.flexPadding view.flexWrap = self.flexWrap view.flex = self.flex view.flexFixedSize = self.flexFixedSize view.flexMinimumSize = self.flexMinimumSize view.flexMaximumSize = self.flexMaximumSize } if let frame = self.frame { view.frame = frame } for (key,value) in self.otherProperty { var theValue: AnyObject = value if let str = value as? String { if let newValue = self.bindTheKeyPath(str, key: key) { theValue = newValue } if theValue as! String == "" { continue } } view.attr(key, value) } } func childLoop(pelement:OGElement){ for element in pelement.children { if let ele = element as? OGElement,let pro = EUIParse.loopElement(ele) { self.subTags.append(pro) } } } func bindTheKeyPath(str:String,key:String) -> String?{ let value = Regex("\\{\\{(\\w+)\\}\\}").replace(str, withBlock: { (regx) -> String in let keyPath = regx.subgroupMatchAtIndex(0)?.trim self.bind[key] = keyPath return "" }) return value } } ================================================ FILE: Pod/Classes/Extend/EUI/EUI.swift ================================================ // // EUI.swift // medical // // Created by zhuchao on 15/5/5. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit public class EUI: NSObject { public class func encode(fileName:String,suffix:String = "xml",toPath:String){ let path = NSBundle.mainBundle().pathForResource(fileName, ofType: suffix)! if NSFileManager.defaultManager().fileExistsAtPath(path) == false{ return } if let str = try? String(contentsOfFile: path, encoding: NSUTF8StringEncoding) { if let encrypt = DesEncrypt.encryptWithText(str, key: CRTPTO_KEY) { var error:NSError? do { try encrypt.writeToFile(toPath, atomically: true, encoding: NSUTF8StringEncoding) EZPrintln("success") } catch let error1 as NSError { error = error1 EZPrintln(error) } } } } public class func setLiveLoad(controller:EUScene,suffix:String){ if IsSimulator && suffix == "xml"{ let paths = self.loadLiveFile(controller,suffix:suffix) if paths?.count > 0 { for path in paths! { watchForChangesToFilePath(path) { self.loadLiveFile(controller,suffix:suffix) controller.eu_viewWillLoad() controller.loadEZLayout() } } } }else{ self.loadHtml(controller, suffix: suffix) } controller.eu_viewWillLoad() } private class func loadLiveFile(controller:EUScene,suffix:String) -> [String]?{ let fileName = controller.nameOfClass let path = NSBundle(path: LIVE_LOAD_PATH)!.pathForResource(fileName, ofType: suffix)! if NSFileManager.defaultManager().fileExistsAtPath(path) == false{ return nil } var paths = Array() paths.append(path) do{ if let html = try? String(contentsOfFile: path, encoding: NSUTF8StringEncoding){ var finalHtml = html if let newHtml = Regex("@import\\(([^\\)]*)\\)").replace(finalHtml,withBlock: { (regx) -> String in let subFile = regx.subgroupMatchAtIndex(0)?.trim let subPath = NSBundle(path: LIVE_LOAD_PATH)!.pathForResource(subFile, ofType: suffix)! if NSFileManager.defaultManager().fileExistsAtPath(subPath) { paths.append(subPath) return try! String(contentsOfFile:subPath, encoding: NSUTF8StringEncoding) }else{ return "" } }) { finalHtml = newHtml } if let regMatchs = Regex("").match(finalHtml) { for regx in regMatchs { if let styleString = regx.subgroupMatchAtIndex(0)?.trim, let regxsubs = Regex("\\.([\\w]*)[\\s]*\\{[\\s]?([^}]*)[\\s]?\\}").match(styleString){ for regxsub in regxsubs { let className = regxsub.subgroupMatchAtIndex(0)!.trim let values = regxsub.subgroupMatchAtIndex(1)!.trim if let aHtml = Regex("@"+className).replace(finalHtml, withTemplate: values) { finalHtml = aHtml } } } } } if let regMatchs = Regex("]*>([\\s\\S]*?)").match(finalHtml) { var scriptStrings = ""; for regx in regMatchs { if let scriptString = regx.subgroupMatchAtIndex(0)?.trim{ scriptStrings += scriptString } } controller.scriptString = scriptStrings } if let cleanHtml = Regex("@[\\w]*").replace(finalHtml, withTemplate: "") { finalHtml = cleanHtml } SwiftTryCatch.`try`({ let body = EUIParse.ParseHtml(finalHtml) var views = [UIView]() for aview in body { views.append(aview.getView()) } controller.eu_subViews = views }, `catch`: { (error) in print(controller.nameOfClass + "Error:\(error.description)") }, finally: nil) }else{ throw NSError(domain: "EasyIOS", code: -1, userInfo: ["err":"can not open "+path.URLString]) } }catch let error as NSError{ print("error is \(error)") } return paths } private class func loadHtml (controller:EUScene,suffix:String){ let fileName = controller.nameOfClass let path = NSBundle(path: BUNDLE_PATH)!.pathForResource(fileName, ofType: suffix)! if NSFileManager.defaultManager().fileExistsAtPath(path) == false{ return } if let html = try? String(contentsOfFile: path, encoding: NSUTF8StringEncoding) { var finalHtml = html if suffix == "crypto" && CRTPTO_KEY != "" { if let aHtml = DesEncrypt.decryptWithText(finalHtml, key: CRTPTO_KEY) { finalHtml = aHtml } } if let newHtml = Regex("@import\\(([^\\)]*)\\)").replace(finalHtml,withBlock: { (regx) -> String in let subFile = regx.subgroupMatchAtIndex(0)?.trim let subPath = NSBundle(path: BUNDLE_PATH)!.pathForResource(subFile, ofType: suffix)! if NSFileManager.defaultManager().fileExistsAtPath(subPath) { return try! String(contentsOfFile:subPath, encoding: NSUTF8StringEncoding) }else{ return "" } }) { finalHtml = newHtml } if let regMatchs = Regex("").match(finalHtml) { for regx in regMatchs { if let styleString = regx.subgroupMatchAtIndex(0)?.trim, let regxsubs = Regex("\\.([\\w]*)[\\s]*\\{[\\s]?([^}]*)[\\s]?\\}").match(styleString){ for regxsub in regxsubs { let className = regxsub.subgroupMatchAtIndex(0)!.trim let values = regxsub.subgroupMatchAtIndex(1)!.trim if let aHtml = Regex("@"+className).replace(finalHtml, withTemplate: values) { finalHtml = aHtml } } } } } if let regMatchs = Regex("]*>([\\s\\S]*?)").match(finalHtml) { var scriptStrings = ""; for regx in regMatchs { if let scriptString = regx.subgroupMatchAtIndex(0)?.trim{ scriptStrings += scriptString } } controller.scriptString = scriptStrings } if let cleanHtml = Regex("@[\\w]*").replace(finalHtml, withTemplate: "") { finalHtml = cleanHtml } SwiftTryCatch.`try`({ let body = EUIParse.ParseHtml(finalHtml) var views = [UIView]() for aview in body { views.append(aview.getView()) } controller.eu_subViews = views }, `catch`: { (error) in print(controller.nameOfClass + "Error:\(error.description)") }, finally: nil) } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUIExtend+UIView.swift ================================================ // // EUIExtend+UIView.swift // medical // // Created by zhuchao on 15/5/1. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import SnapKit import Bond import Kingfisher import TTTAttributedLabel private var UIViewTagIdHandle :UInt8 = 1 private var UIViewViewPropertyHandle :UInt8 = 2 private var UIViewConstraintGroupHandle :UInt8 = 3 private var UIViewWatchHandle :UInt8 = 4 private var UIViewDisposeBag :UInt8 = 5 //private var CollectionViewDataSourceBond :UInt8 = 6 private var attributedLabelDelegateHandle :UInt8 = 7 public class TTTAttributedLabelDelegateHandle:NSObject,TTTAttributedLabelDelegate{ } extension EUScene{ public var attributedLabelDelegate:TTTAttributedLabelDelegateHandle? { get{ if let d: AnyObject = objc_getAssociatedObject(self, &attributedLabelDelegateHandle) { return d as? TTTAttributedLabelDelegateHandle }else{ return nil } }set (value){ objc_setAssociatedObject(self, &attributedLabelDelegateHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public func eu_viewByTag(tagId:String) ->UIView?{ if let subViews = self.eu_subViews { for view in subViews { if view.subviews.count > 0 { if let aview = view.getSubViewByTagId(tagId){ return aview } } if view.tagProperty.tagId == tagId { return view } } } return nil } func loadEZLayout(){ self.view.clearEZView() if let subViews = self.eu_subViews { for view in subViews { self.view.addSubview(view) } self.view.subRender(self) } self.context.setObject(self, forKeyedSubscript: "document") self.eval(self.scriptString) self.eu_viewDidLoad() } } extension UIView { public var disposeBag:DisposeBag?{ get{ if let d: AnyObject = objc_getAssociatedObject(self, &UIViewDisposeBag) { return d as? DisposeBag }else{ return nil } }set(b){ objc_setAssociatedObject(self, &UIViewDisposeBag, b, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public class func formTag(tag:String) -> UIView { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) { return view } } return UIView() } func clearEZView(){ for view in self.subviews { let aview = view aview.clearEZView() aview.removeFromSuperview() } } var tagProperty:ViewProperty{ get{ if let d: AnyObject = objc_getAssociatedObject(self, &UIViewViewPropertyHandle) { return d as! ViewProperty }else{ return ViewProperty() } }set (value){ objc_setAssociatedObject(self, &UIViewViewPropertyHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } public func getSubViewByTagId(tagId:String) -> UIView?{ for view in self.subviews { if view.subviews.count > 0 { if let aview = view.getSubViewByTagId(tagId){ return aview } } if view.tagProperty.tagId == tagId { return view } } return nil } public func getRootView() -> UIView { if self.superview == nil { return self }else{ return self.superview!.getRootView() } } func getViewById(tagId:String) -> UIView? { if tagId == ""{ return nil }else if tagId == Constrain.targetRoot { return self.getRootView() }else if tagId == Constrain.targetSuper { return self.superview }else if tagId == Constrain.targetSelf || tagId == self.tagProperty.tagId{ return self }else{ return self.getRootView().getSubViewByTagId(tagId) } } func subRender(scene:EUScene) { for subView in self.subviews { let view = subView view.renderTheView(scene) } } func renderTheView(scene:EUScene){ self.subRender(scene) self.renderSelector(scene) self.renderGesture(scene) self.renderLayout() } public func renderDataBinding(scene:EUScene,bind:NSObject?){ for subView in self.subviews { subView.renderDataBinding(scene,bind: bind) } let property = self.tagProperty as ViewProperty if let bindKey = property.bind["background-color"] { if let color = bind!.valueForKey(bindKey) as? EZColor { color.dym!.bindTo(self.bnd_backgroundColor) } } if let bindKey = property.bind["alpha"] { if let alpha = bind!.valueForKey(bindKey) as? EZFloat { alpha.dym!.bindTo(self.bnd_alpha) } } if let bindKey = property.bind["hidden"] { if let hidden = bind!.valueForKey(bindKey) as? EZBool { hidden.dym!.bindTo(self.bnd_hidden) } } self.disposeBag?.dispose() if let selector = property.onTapBind { if self.disposeBag == nil { self.disposeBag = DisposeBag() } self.disposeBag?.addDisposable(self.whenTap(selector.tapNumber){ let script = Regex("\\{\\{(\\w+)\\}\\}").replace(selector.selector, withBlock: { (regx) -> String in let bindKey = regx.subgroupMatchAtIndex(0)?.trim if let value = bind!.valueForKey(bindKey!) as? String{ return value }else if let value = bind!.valueForKey(bindKey!) as? Int{ return String(value) }else if let value = bind!.valueForKey(bindKey!) as? Bool{ return value ? "true" : "false" } return "" }) scene.eval(script) }) } if let selector = property.onSwipeBind { if self.disposeBag == nil { self.disposeBag = DisposeBag() } self.disposeBag?.addDisposable(self.whenSwipe(selector.numberOfTouches, direction: selector.direction){ let script = Regex("\\{\\{(\\w+)\\}\\}").replace(selector.selector, withBlock: { (regx) -> String in let bindKey = regx.subgroupMatchAtIndex(0)?.trim if let value = bind!.valueForKey(bindKey!) as? String{ return value }else if let value = bind!.valueForKey(bindKey!) as? Int{ return String(value) }else if let value = bind!.valueForKey(bindKey!) as? Bool{ return value ? "true" : "false" } return "" }) scene.eval(script) }) } } func renderGesture(scene:EUScene){ let property = self.tagProperty as ViewProperty self.disposeBag?.dispose() if let selector = property.onTap { if self.disposeBag == nil { self.disposeBag = DisposeBag() } self.disposeBag?.addDisposable(self.whenTap(selector.tapNumber){ scene.eval(selector.selector) }) } if let selector = property.onSwipe { if self.disposeBag == nil { self.disposeBag = DisposeBag() } self.disposeBag?.addDisposable(self.whenSwipe(selector.numberOfTouches, direction: selector.direction){ scene.eval(selector.selector) }) } } func renderSelector(scene:EUScene){ } func renderLayout(){ var consList = self.tagProperty.align + self.tagProperty.margin if let width = self.tagProperty.width { consList.append(width) } if let height = self.tagProperty.height { consList.append(height) } if consList.count == 0 { return } self.snp_remakeConstraints(){[unowned self] (make) -> Void in for cons in consList { let targetView = self.getViewById(cons.target) let value = cons.value let key = cons.constrainName if targetView != nil { switch key { case .AlignRight: make.right.equalTo(targetView!).offset(value) case .AlignLeft: make.left.equalTo(targetView!).offset(value) case .AlignTop: make.top.equalTo(targetView!).offset(value) case .AlignBottom: make.bottom.equalTo(targetView!).offset(value) case .AlignCenterX: make.centerX.equalTo(targetView!).offset(value) case .AlignCenterY: make.centerY.equalTo(targetView!).offset(value) case .MarginTop: make.top.equalTo(targetView!.snp_bottom).offset(value) case .MarginLeft: make.left.equalTo(targetView!.snp_right).offset(value) case .MarginRight: make.right.equalTo(targetView!.snp_left).offset(-value) case .MarginBottom: make.bottom.equalTo(targetView!.snp_top).offset(-value) case .Width: make.width.equalTo(targetView!.snp_width).multipliedBy(value) case .Height: make.height.equalTo(targetView!.snp_height).multipliedBy(value) default: EZPrintln("remake default") } }else { switch key { case .Width: make.width.equalTo(Int(value)) case .Height: make.height.equalTo(Int(value)) default: EZPrintln("remake default") } } } } } } extension UIImageView { override public class func formTag(tag:String) -> UIImageView { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UIImageView { return view } } return UIImageView() } override public func renderDataBinding(scene:EUScene,bind:NSObject?){ super.renderDataBinding(scene,bind: bind) if let bindKey = self.tagProperty.bind["src"] { if let image = bind!.valueForKey(bindKey) as? EZImage { image.dym!.bindTo(self.bnd_image) }else if let src = bind!.valueForKey(bindKey) as? EZURL { src.dym!.bindTo(self.bnd_URLImage) }else if let image = bind!.valueForKey(bindKey) as? UIImage { self.image = image }else if let url = bind!.valueForKey(bindKey) as? NSURL { self.kf_setImageWithURL(url) }else if let str = bind!.valueForKey(bindKey) as? String { if let image = UIImage(named: str) { self.image = image }else if let url = NSURL(string: str) { self.kf_setImageWithURL(url) } } }else if let bindKey = self.tagProperty.bind["image"] { if let image = bind!.valueForKey(bindKey) as? EZImage { image.dym!.bindTo(self.bnd_image) }else if let image = bind!.valueForKey(bindKey) as? UIImage { self.image = image } } } } extension UILabel { override public class func formTag(tag:String) -> UILabel { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UILabel { return view } } return UILabel() } override public func renderDataBinding(scene:EUScene,bind:NSObject?){ super.renderDataBinding(scene,bind: bind) if let bindKey = self.tagProperty.bind["text"] { if let text = bind!.valueForKey(bindKey) as? EZAttributedString { text.dym!.bindTo(self.bnd_attributedText) }else if let text = bind!.valueForKey(bindKey) as? EZString { text.dym!.bindTo(self.bnd_text) }else if let text = bind!.valueForKey(bindKey) as? String { self.text = text }else if let data = bind!.valueForKey(bindKey) as? NSData { self.attributedText = NSAttributedString(fromHTMLData: data, attributes: ["dict":self.tagProperty.style]) }else if let string = bind!.valueForKey(bindKey) as? NSAttributedString { self.attributedText = string } } if let bindKey = self.tagProperty.bind["text-color"] { if let color = bind!.valueForKey(bindKey) as? EZColor { color.dym!.bindTo(self.bnd_textColor) }else if let color = bind!.valueForKey(bindKey) as? UIColor { self.textColor = color } } } } extension TTTAttributedLabel { override public class func formTag(tag:String) -> TTTAttributedLabel { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? TTTAttributedLabel { return view } } return TTTAttributedLabel(frame: CGRectZero) } override public func renderDataBinding(scene:EUScene,bind:NSObject?){ super.renderDataBinding(scene,bind: bind) if let bindKey = self.tagProperty.bind["TTText"] { if let text = bind!.valueForKey(bindKey) as? EZAttributedString { text.dym! ->> self.dynTTTAttributedText }else if let text = bind!.valueForKey(bindKey) as? EZString { text.dym! ->> self.dynTTText }else if let data = bind!.valueForKey(bindKey) as? EZData { data.dym! ->> self.dynTTTData }else if let text = bind!.valueForKey(bindKey) as? String { self.setText(text) }else if let data = bind!.valueForKey(bindKey) as? NSData { self.setText(NSAttributedString(fromHTMLData: data, attributes: ["dict":self.tagProperty.style])) }else if let string = bind!.valueForKey(bindKey) as? NSAttributedString { self.setText(string) } } } override func renderSelector(scene:EUScene){ if let delegate = scene.attributedLabelDelegate { self.delegate = delegate } } } extension UITextField { override public class func formTag(tag:String) -> UITextField { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UITextField { return view } } return UITextField() } override public func renderDataBinding(scene:EUScene,bind:NSObject?){ super.renderDataBinding(scene,bind:bind) if let bindKey = self.tagProperty.bind["text"] { if let text = bind!.valueForKey(bindKey) as? EZString { text.dym!.bindTo(self.bnd_text) }else if let text = bind!.valueForKey(bindKey) as? String { self.text = text } } } } extension UIButton { override public class func formTag(tag:String) -> UIButton { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UIButton { return view } } return UIButton() } override func renderSelector(scene:EUScene){ let property = self.tagProperty as? ButtonProperty if let selector = property?.onEvent { self.bnd_controlEvent.filter{$0 == selector.event}.map{ e in}.observe({ scene.eval(selector.selector) }) } } } private var UITableViewCellHandle :UInt8 = 5 extension UITableView { override public class func formTag(tag:String) -> UITableView { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UITableView { return view } } return UITableView() } public func getSectionViewByTagId(tagId:String,target:EUScene,bind:NSObject? = nil) -> UIView?{ let property = self.tagProperty as? TableViewProperty if let pro = property?.sectionView[tagId] { let view = pro.getView() view.renderTheView(target) view.renderDataBinding(target,bind: bind) return view } return nil } var reusableViews:Dictionary{ get{ if let d: AnyObject = objc_getAssociatedObject(self, &UITableViewCellHandle) { return d as! Dictionary }else{ return Dictionary() } }set (value){ objc_setAssociatedObject(self, &UITableViewCellHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } override func subRender(scene:EUScene) { scene.eu_tableViewDidLoad(self) } public func dequeueReusableCell(reuseId:String,forIndexPath:NSIndexPath,target:EUScene,bind:NSObject? = nil) -> UITableViewCell{ let cell = self.dequeueReusableCellWithIdentifier(reuseId, forIndexPath: forIndexPath) if cell.contentView.subviews.count == 0 { SwiftTryCatch.`try`({ let property = self.tagProperty as! TableViewProperty if let cellProperty = property.reuseCell[reuseId] { let view = cellProperty.getView() cell.contentView.addSubview(view) view.renderTheView(target) }}, `catch`: { (error) in print(self.nameOfClass + "Error:\(error.description)") }, finally:nil) } if let view = cell.contentView.subviews.first { view.renderDataBinding(target,bind: bind) } return cell } } private var UICollectionViewCellHandle :UInt8 = 5 extension UICollectionView { override public class func formTag(tag:String) -> UICollectionView { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UICollectionView { return view } } return UICollectionView() } var reusableViews:Dictionary{ get{ if let d: AnyObject = objc_getAssociatedObject(self, &UICollectionViewCellHandle) { return d as! Dictionary }else{ return Dictionary() } }set (value){ objc_setAssociatedObject(self, &UICollectionViewCellHandle, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } override func subRender(scene:EUScene) { scene.eu_collectionViewDidLoad(self) } public func dequeueReusableCell(reuseId:String,forIndexPath:NSIndexPath,target:EUScene,bind:NSObject? = nil) -> UICollectionViewCell{ let cell = self.dequeueReusableCellWithReuseIdentifier(reuseId, forIndexPath: forIndexPath) if cell.contentView.subviews.count == 0 { SwiftTryCatch.`try`({ let property = self.tagProperty as! CollectionViewProperty if let cellProperty = property.reuseCell[reuseId] { let view = cellProperty.getView() cell.contentView.addSubview(view) view.renderTheView(target) }}, `catch`: { (error) in print(self.nameOfClass + "Error:\(error.description)") }, finally: nil) } if let view = cell.contentView.subviews.first{ view.renderDataBinding(target,bind:bind) } return cell } } extension UIScrollView { override public class func formTag(tag:String) -> UIScrollView { if let scene = URLNavigation.currentViewController() as? EUScene { if let view = scene.eu_viewByTag(tag) as? UIScrollView { return view } } return UIScrollView() } override func renderSelector(scene:EUScene){ let property = self.tagProperty as? ScrollViewProperty if let pullRefresh = property?.pullToRefresh { if pullRefresh.viewClass.characters.isEmpty { self.addPullToRefreshWithActionHandler(){ scene.eval(pullRefresh.selector) } }else if let view = NSObject(fromString: pullRefresh.viewClass) as? UIView { self.addPullToRefreshWithActionHandler(view) { scene.eval(pullRefresh.selector) } } } if let infiniteScrolling = property?.infiniteScrolling { if infiniteScrolling.viewClass.characters.isEmpty { self.addInfiniteScrollingWithActionHandler(){ scene.eval(infiniteScrolling.selector) } }else if let view = NSObject(fromString: infiniteScrolling.viewClass) as? UIView { self.addInfiniteScrollingWithActionHandler(view) { scene.eval(infiniteScrolling.selector) } } } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUIKit.swift ================================================ // // EUIView.swift // Pods // // Created by zhuchao on 15/7/21. // // import CoreImage import UIKit import JavaScriptCore @objc public protocol EUIView:JSExport,ENSObject{ } @objc public protocol EUIImageView:JSExport,EUIView,ENSObject{ init(image: UIImage!) init(image: UIImage!, highlightedImage: UIImage?) } @objc public protocol EUITextField:JSExport,EUIView,ENSObject{ } @objc public protocol EUIButton:JSExport,EUIView,ENSObject{ } @objc public protocol EUIScrollView:JSExport,EUIView,ENSObject{ } @objc public protocol EUITableView:JSExport,EUIScrollView,ENSObject{ } @objc public protocol EUICollectionView:JSExport,EUIScrollView,ENSObject{ } @objc public protocol EUILabel:JSExport,EUIView,ENSObject{ } @objc public protocol EUIColor:JSExport,ENSObject{ static func blackColor() -> UIColor // 0.0 white static func darkGrayColor() -> UIColor // 0.333 white static func lightGrayColor() -> UIColor // 0.667 white static func whiteColor() -> UIColor // 1.0 white static func grayColor() -> UIColor // 0.5 white static func redColor() -> UIColor // 1.0, 0.0, 0.0 RGB static func greenColor() -> UIColor // 0.0, 1.0, 0.0 RGB static func blueColor() -> UIColor // 0.0, 0.0, 1.0 RGB static func cyanColor() -> UIColor // 0.0, 1.0, 1.0 RGB static func yellowColor() -> UIColor // 1.0, 1.0, 0.0 RGB static func magentaColor() -> UIColor // 1.0, 0.0, 1.0 RGB static func orangeColor() -> UIColor // 1.0, 0.5, 0.0 RGB static func purpleColor() -> UIColor // 0.5, 0.0, 0.5 RGB static func brownColor() -> UIColor // 0.6, 0.4, 0.2 RGB static func clearColor() -> UIColor // 0.0 white, 0.0 alpha } @objc public protocol EUIImage:JSExport,ENSObject{ init(named name: String)// load from main bundle @available(iOS, introduced=8.0) init(named name: String, inBundle bundle: NSBundle?, compatibleWithTraitCollection traitCollection: UITraitCollection?) init?(contentsOfFile path: String) } ================================================ FILE: Pod/Classes/Extend/EUI/EUIParse.swift ================================================ // // EZUI.swift // medical // // Created by zhuchao on 15/4/30. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import SnapKit class EUIParse: NSObject { class func ParseHtml(html:String) -> [ViewProperty]{ let data = ObjectiveGumbo.parseDocumentWithString(html) let body = data.elementsWithTag(GUMBO_TAG_BODY).first as! OGElement var viewArray = [ViewProperty]() for element in body.children { if element.isKindOfClass(OGElement) { if let pro = self.loopElement(element as! OGElement) { viewArray.append(pro) } } } return viewArray } // 对子节点进行递归解析 class func loopElement(pelement:OGElement) -> ViewProperty?{ var tagProperty:ViewProperty? var type:String? switch (pelement.tag.rawValue){ case GUMBO_TAG_IMG.rawValue : type = "UIImageView" case GUMBO_TAG_SPAN.rawValue : type = "UILabel" case GUMBO_TAG_BUTTON.rawValue : type = "UIButton" case GUMBO_TAG_INPUT.rawValue : type = "UITextField" default : type = self.string(pelement,key:"type") } if let atype = type { switch (atype){ case "UIScrollView","scroll": tagProperty = ScrollViewProperty() case "UITableView","table": tagProperty = TableViewProperty() case "UICollectionView","collection": tagProperty = CollectionViewProperty() case "UIImageView","imageView": tagProperty = ImageProperty() case "UILabel","label": tagProperty = LabelProperty() case "UIButton","button": tagProperty = ButtonProperty() case "UITextField","field": tagProperty = TextFieldProperty() default : tagProperty = ViewProperty() } }else{ tagProperty = ViewProperty() } if tagProperty != nil { tagProperty!.renderTag(pelement) } return tagProperty } class func string (element:OGElement,key:String) ->String?{ if let str = element.attributes?[key] as? String { return str } return nil } class func getStyleProperty (element:OGElement,key:String) -> Array? { if element.attributes?[key] == nil { return nil } var style = Array() let origin = element.attributes?[key] as! String let firstArray = origin.trimArrayBy(";") for str in firstArray { var secondArray = str.trimArrayBy(":") if secondArray.count == 2 { let raw = secondArray[0] as String let rawKey = CSS(rawValue: key+"-"+raw.trim) if rawKey == nil { continue } var values = secondArray[1].trimArray if key == "align" { switch rawKey! { case .AlignTop,.AlignBottom,.AlignLeft,.AlignRight,.AlignCenterX,.AlignCenterY: if values.count == 1 { style.append(Constrain(name:rawKey!,value: values[0].trim.floatValue)) }else if(values.count >= 2){ style.append(Constrain(name:rawKey!,value: values[0].trim.floatValue,target:values[1].trim)) } case .AlignCenter: if values.count >= 1 { style.append(Constrain(name:.AlignCenterX,value: values[0].trim.floatValue)) } if values.count >= 2 { style.append(Constrain(name:.AlignCenterY,value: values[1].trim.floatValue)) } case .AlignEdge: if values.count >= 1 && values[0].trim != "*" { style.append(Constrain(name:.AlignTop,value: values[0].trim.floatValue)) } if values.count >= 2 && values[1].trim != "*" { style.append(Constrain(name:.AlignLeft,value: values[1].trim.floatValue)) } if values.count >= 3 && values[2].trim != "*" { style.append(Constrain(name:.AlignBottom,value: values[2].trim.floatValue)) } if values.count >= 4 && values[3].trim != "*" { style.append(Constrain(name:.AlignRight,value: values[3].trim.floatValue)) } default : print(raw.trim + " is jumped") } }else if key == "margin" { switch rawKey! { case .MarginTop,.MarginBottom,.MarginLeft,.MarginRight: if values.count == 1 { style.append(Constrain(name:rawKey!,value: values[0].trim.floatValue)) }else if(values.count >= 2){ style.append(Constrain(name:rawKey!,value: values[0].trim.floatValue,target:values[1].trim)) } default : print(raw.trim + " is jumped") } } }else if secondArray.count == 1 && key == "align" { var values = secondArray[0].trimArray if values.count >= 1 && values[0].trim != "*" { style.append(Constrain(name:.AlignTop,value: values[0].trim.floatValue)) } if values.count >= 2 && values[1].trim != "*" { style.append(Constrain(name:.AlignLeft,value: values[1].trim.floatValue)) } if values.count >= 3 && values[2].trim != "*" { style.append(Constrain(name:.AlignBottom,value: values[2].trim.floatValue)) } if values.count >= 4 && values[3].trim != "*" { style.append(Constrain(name:.AlignRight,value: values[3].trim.floatValue)) } } } return style } } ================================================ FILE: Pod/Classes/Extend/EUI/EUIProperty.swift ================================================ // // EUIProperty.swift // medical // // Created by zhuchao on 15/4/30. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit enum CSS:String{ case MarginTop = "margin-top" case MarginLeft = "margin-left" case MarginRight = "margin-right" case MarginBottom = "margin-bottom" case AlignTop = "align-top" case AlignBottom = "align-bottom" case AlignLeft = "align-left" case AlignRight = "align-right" case AlignCenter = "align-center" case AlignCenterX = "align-center-x" case AlignCenterY = "align-center-y" case AlignEdge = "align-edge" case Width = "width" case Height = "height" } struct Constrain{ static var targetSelf = "self" static var targetSuper = "super" static var targetRoot = "root" var value:CGFloat = 0.0 var target = targetSuper var constrainName = CSS.AlignEdge init(name:CSS,value:CGFloat,target:String = targetSuper){ self.constrainName = name self.value = value self.target = target } } struct SelectorAction{ var selector:String = "" var event:UIControlEvents = UIControlEvents.TouchUpInside init(selector:String,event:String = "TouchUpInside"){ self.selector = selector self.event = event.controlEvent } } struct PullRefreshAction { var selector:String = "" var viewClass:String = "" init(selector:String,viewClass:String = ""){ self.selector = selector self.viewClass = viewClass } } struct InfiniteScrollingAction { var selector:String = "" var viewClass:String = "" init(selector:String,viewClass:String = ""){ self.selector = selector self.viewClass = viewClass } } struct TapGestureAction{ var selector:String = "" var tapNumber:NSInteger = 1 init(selector:String,tapNumber:String = "1"){ self.selector = selector self.tapNumber = tapNumber.integerValue } } struct SwipeGestureAction { var direction: UISwipeGestureRecognizerDirection var selector:String var numberOfTouches:Int = 1 init(selector:String,direction:String,numberOfTouches:String = "1"){ self.selector = selector self.numberOfTouches = numberOfTouches.integerValue switch direction.lowercaseString { case "up" : self.direction = .Up case "down" : self.direction = .Down case "right" : self.direction = .Right case "left" : self.direction = .Left default : self.direction = .Up } } } ================================================ FILE: Pod/Classes/Extend/EUI/EUScene.swift ================================================ // // EUScene.swift // medical // // Created by zhuchao on 15/5/6. // Copyright (c) 2015年 zhuchao. All rights reserved. // import UIKit import JavaScriptCore public var LIVE_LOAD_PATH = NSBundle.mainBundle().pathForResource("xml", ofType: "bundle")! public var BUNDLE_PATH = NSBundle.mainBundle().pathForResource("xml", ofType: "bundle")! public var CRTPTO_KEY = "" @objc protocol EUSceneExport:JSExport { func getElementById(id:String) -> UIView } public class EUScene: EZScene,EUSceneExport{ public func getElementById(id:String) -> UIView { return UIView.formTag(id) } public var SUFFIX = "xml" public var eu_subViews:[UIView]? public var scriptString:String? public var context = EZJSContext() public func define(funcName:String,actionBlock:@convention(block) ()->Void){ context.define(funcName, actionBlock: actionBlock) } public func eval(script: String?) -> JSValue?{ if let str = script { var result:JSValue? SwiftTryCatch.`try`({ result = self.context.evaluateScript(str) }, `catch`: { (error) in print("JS Error:\(error.description)") }, finally: nil) return result }else{ return nil } } override public func loadView() { super.loadView() EUI.setLiveLoad(self,suffix: SUFFIX) } override public func viewDidLoad() { super.viewDidLoad() self.automaticallyAdjustsScrollViewInsets = false; self.extendedLayoutIncludesOpaqueBars = true; self.edgesForExtendedLayout = UIRectEdge.All; self.view.backgroundColor = UIColor.whiteColor() self.loadEZLayout() } public func eu_viewWillLoad(){ } public func eu_viewDidLoad(){ } public func eu_tableViewDidLoad(tableView:UITableView?){ } public func eu_collectionViewDidLoad(collectionView:UICollectionView?){ } } ================================================ FILE: Pod/Classes/Extend/EUI/EZJSCore.swift ================================================ import UIKit import JavaScriptCore @objc protocol ConsoleExport:JSExport { static func log(object:AnyObject?) } @objc public class Console:NSObject,ConsoleExport { static public func log(object:AnyObject?){ if let obj: AnyObject = object{ print(obj) } } } @objc protocol JSURLManagerExport:JSExport { static func present(url:String,_ animated:Bool) static func push(url:String,_ animated:Bool) static func dismiss(animated:Bool) } public class JSURLManager:NSObject,JSURLManagerExport { static public func present(url:String,_ animated:Bool){ let viewController = UIViewController.initFromString(url, fromConfig: URLManager.shareInstance().config) let nav = EZNavigationController(rootViewController: viewController) URLNavigation.presentViewController(nav, animated: animated) } static public func push(url:String,_ animated:Bool){ URLManager.pushURLString(url, animated: animated) } static public func dismiss(animated:Bool){ URLNavigation.dismissCurrentAnimated(animated) } } public class EZJSContext:JSContext{ override init(){ super.init() self.setObject(Console.self, forKeyedSubscript: "console") self.setObject(JSURLManager.self, forKeyedSubscript: "um") class_addProtocol(EZAction.self, EZActionJSExport.self) self.setObject(EZAction.self, forKeyedSubscript: "EZAction") class_addProtocol(UIColor.self, EUIColor.self) self.setObject(UIColor.self, forKeyedSubscript: "UIColor") class_addProtocol(UIImage.self, EUIImage.self) self.setObject(UIImage.self, forKeyedSubscript: "UIImage") class_addProtocol(UIView.self, EUIView.self) self.setObject(UIView.self, forKeyedSubscript: "UIView") class_addProtocol(UIImageView.self, EUIImageView.self) self.setObject(UIImageView.self, forKeyedSubscript: "UIImageView") class_addProtocol(UITextField.self, EUITextField.self) self.setObject(UITextField.self, forKeyedSubscript: "UITextField") class_addProtocol(UIButton.self, EUIButton.self) self.setObject(UIButton.self, forKeyedSubscript: "UIButton") class_addProtocol(UILabel.self, EUILabel.self) self.setObject(UILabel.self, forKeyedSubscript: "UILabel") class_addProtocol(UIScrollView.self, EUIScrollView.self) self.setObject(UIScrollView.self, forKeyedSubscript: "UIScrollView") class_addProtocol(UITableView.self, EUITableView.self) self.setObject(UITableView.self, forKeyedSubscript: "UITableView") class_addProtocol(UICollectionView.self, EUICollectionView.self) self.setObject(UICollectionView.self, forKeyedSubscript: "UICollectionView") self.exceptionHandler = { context, exception in print("JS Error: \(exception)") } } override init(virtualMachine: JSVirtualMachine!) { super.init(virtualMachine:virtualMachine) } public func define(funcName:String,actionBlock:@convention(block) ()->Void){ self.setObject(unsafeBitCast(actionBlock, AnyObject.self), forKeyedSubscript:funcName) } } ================================================ FILE: Pod/Classes/Extend/EUI/NSAttributedString+HTMLStyle.h ================================================ // // NSAttributedString+HTMLStyle.h // QRContentMobilizer // // Created by Wojciech Czekalski on 22.03.2014. // Copyright (c) 2014 Wojciech Czekalski. All rights reserved. // #import typedef NSString QRHTMLAttribute; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeParagraph; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeLink; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader1; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader2; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader3; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader4; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader5; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader6; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeEmphasis; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeStrong; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeBold; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAlternateVoice; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSmall; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSubscripted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSuperscripted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeInserted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeDeleted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHighlighted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeCode; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeKeyboardText; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSampleCode; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeVariable; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributePreformatted; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAbbreviation; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAddress; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeBlockQuote; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeInlineQuote; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeTitle; FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeDefinition; /** * Creation of NSAttributedString out of HTML with per-tag attributes made easy. */ @interface NSAttributedString (HTMLStyle) /** * Creates an attributed string from HTML data. Assumes `NSUTF8StringEncoding`. * * @param data Data to be processed. * * @return Returns a new instance of `NSAttributedString` or nil if given data was invalid. */ + (instancetype)attributedStringFromHTMLData:(NSData *)data; /** * Creates an attributed string from HTML string and CSS string with attributes. * * @param html A string containing HTML. * @param css A CSS string. If nil, use attributedStringFromHTMLData: instead. * * @abstract Don't convert your HTML and CSS into NSStrings from NSData objects. Use `attributedStringFromHTMLData:CSSData:` instead. * * @return Returns a new instance of `NSAttributedString` or nil if given data was invalid. */ + (instancetype)attributedStringFromHTMLString:(NSString *)html CSSString:(NSString *)css; /** * Creates an attributed string from merged HTML and CSS data. * * @param html HTML data to be processed * @param css CSS data to be processed * * @return Returns a new instance of NSAttributedString or nil if given data was invalid. */ + (instancetype)attributedStringFromHTMLData:(NSData *)html CSSData:(NSData *)css; /** * Creates an attributed string from merged HTML and CSS files. * * @param html URL of a CSS file. * @param css URL of a CSS file. * * @return Returns a new instance of NSAttributedString or nil if given data was invalid. */ + (instancetype)attributedStringFromHTMLFileAtURL:(NSURL *)html CSSURL:(NSURL *)css; /** * Creates an attributed string from HTML data and attributes parsed from attributes dict. * * @param data HTML data to be processed * @param attributes Key-Value pairs of NSAttributedString attributes keyed under HTML tags. * * @return Returns a new instance of NSAttributedString or nil if given data was invalid. */ + (instancetype)attributedStringFromHTMLData:(NSData *)data attributes:(NSDictionary *)attributes; @end /** * Convinience methods for dealing with adding attributes to `NSAttributedStrings` from HTML. */ @interface NSMutableDictionary (CSS) /** * Sets attributes for a given HTML tag. * * @param attributes NSDictionary containing key-value pairs of `NSAttributedString` attributes. * @param tag `HTML` tag to be assigned to the attributes * @param flatten if flatten set to yes, the dictionary is converted into CSS string before being added to the receiver. */ - (void)addAttributes:(NSDictionary *)attributes forHTMLAttribute:(QRHTMLAttribute *)tag flatten:(BOOL)flatten; @end @interface NSDictionary (CSS) /** * Parses receiver and creates a `NSString` with `CSS` attributes. * * @return Returns a `NSString` with `CSS` attributes. */ - (NSString *)CSSStringFromAttributes; @end /** * NSData category which adds method allowing to replace occurencies of given data with another data. */ @interface NSData (HTMLAdditions) /** * Creates `NSData` instance with replaced occurencies. * * @param data `NSData` to be replaced. * @param replacementData `NSData` to be inserted in place of `data`. * * This method replaces all occurencies of `data` with `replacementData`. * * @return Returns `NSData` with replaced contents. */ - (NSData *)dataByReplacingOccurrencesOfData:(NSData *)data withData:(NSData *)replacementData; @end ================================================ FILE: Pod/Classes/Extend/EUI/NSAttributedString+HTMLStyle.m ================================================ // // NSAttributedString+HTMLStyle.m // QRContentMobilizer // // Created by Wojciech Czekalski on 22.03.2014. // Copyright (c) 2014 Wojciech Czekalski. All rights reserved. // #import "NSAttributedString+HTMLStyle.h" #import "regex.h" #import QRHTMLAttribute * const QRHTMLAttributeParagraph = @"p"; QRHTMLAttribute * const QRHTMLAttributeLink = @"a"; QRHTMLAttribute * const QRHTMLAttributeHeader1 = @"h1"; QRHTMLAttribute * const QRHTMLAttributeHeader2 = @"h2"; QRHTMLAttribute * const QRHTMLAttributeHeader3 = @"h3"; QRHTMLAttribute * const QRHTMLAttributeHeader4 = @"h4"; QRHTMLAttribute * const QRHTMLAttributeHeader5 = @"h5"; QRHTMLAttribute * const QRHTMLAttributeHeader6 = @"h6"; QRHTMLAttribute * const QRHTMLAttributeEmphasis = @"em"; QRHTMLAttribute * const QRHTMLAttributeStrong = @"strong"; QRHTMLAttribute * const QRHTMLAttributeBold = @"b"; QRHTMLAttribute * const QRHTMLAttributeAlternateVoice = @"i"; QRHTMLAttribute * const QRHTMLAttributeSmall = @"small"; QRHTMLAttribute * const QRHTMLAttributeSubscripted = @"sub"; QRHTMLAttribute * const QRHTMLAttributeSuperscripted = @"sup"; QRHTMLAttribute * const QRHTMLAttributeInserted = @"ins"; QRHTMLAttribute * const QRHTMLAttributeDeleted = @"del"; QRHTMLAttribute * const QRHTMLAttributeHighlighted = @"mark"; QRHTMLAttribute * const QRHTMLAttributeCode = @"code"; QRHTMLAttribute * const QRHTMLAttributeKeyboardText = @"kbd"; QRHTMLAttribute * const QRHTMLAttributeSampleCode = @"samp"; QRHTMLAttribute * const QRHTMLAttributeVariable = @"var"; QRHTMLAttribute * const QRHTMLAttributePreformatted = @"pre"; QRHTMLAttribute * const QRHTMLAttributeAbbreviation = @"abbr"; QRHTMLAttribute * const QRHTMLAttributeAddress = @"address"; QRHTMLAttribute * const QRHTMLAttributeBlockQuote = @"blockquote"; QRHTMLAttribute * const QRHTMLAttributeInlineQuote = @"q"; QRHTMLAttribute * const QRHTMLAttributeTitle = @"cite"; QRHTMLAttribute * const QRHTMLAttributeDefinition = @"dfn"; @implementation NSAttributedString (HTMLStyle) + (instancetype)attributedStringFromHTMLFileAtURL:(NSURL *)html CSSURL:(NSURL *)css { return [self attributedStringFromHTMLData:[NSData dataWithContentsOfURL:html] CSSData:[NSData dataWithContentsOfURL:css]]; } + (instancetype)attributedStringFromHTMLString:(NSString *)html CSSString:(NSString *)css { return [self attributedStringFromHTMLData:[html dataUsingEncoding:NSUTF8StringEncoding] CSSData:[css dataUsingEncoding:NSUTF8StringEncoding]]; } + (instancetype)attributedStringFromHTMLData:(NSData *)data { return [[self alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:NULL error:nil]; } + (instancetype)attributedStringFromHTMLData:(NSData *)html CSSData:(NSData *)css { if (css) { NSMutableData *newHTMLData = [css mutableCopy]; [newHTMLData appendData:html]; return [self attributedStringFromHTMLData:newHTMLData]; } return [self attributedStringFromHTMLData:html]; } + (instancetype)attributedStringFromHTMLData:(NSData *)data attributes:(NSDictionary *)attributes { return [self attributedStringFromHTMLData:data CSSData:[[attributes CSSStringFromAttributes] dataUsingEncoding:NSUTF8StringEncoding]]; } @end @interface UIColor (HTMLRGBString) - (NSString *)HTMLRGBString; @end @implementation UIColor (HTMLRGBString) - (NSString *)HTMLRGBString { UIColor *color = self; CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; if (CGColorGetNumberOfComponents(color.CGColor) == 2) { CGFloat white = 0.f, alpha = 0.f; [color getWhite:&white alpha:&alpha]; red = white; green = white; blue = white; } else { [color getRed:&red green:&green blue:&blue alpha:&alpha]; } return [NSString stringWithFormat:@"rgb(%li, %li, %li)", lroundf(red*255.f), lroundf(green*255.f), lroundf(blue*255.f)]; } @end @implementation NSDictionary (CSS) + (NSString *)cssStringWithValues:(NSDictionary *)attributes HTMLTag:(QRHTMLAttribute *)tag { NSMutableString *cssString = [NSMutableString stringWithFormat:@"%@ {", tag]; for (NSString *key in attributes) { #if DEBUG NSSet *set = [NSSet setWithObjects:NSLigatureAttributeName, NSKernAttributeName, NSStrikethroughStyleAttributeName, NSStrikethroughColorAttributeName, NSUnderlineStyleAttributeName, NSStrokeColorAttributeName, NSStrokeWidthAttributeName, NSTextEffectAttributeName, NSObliquenessAttributeName, NSExpansionAttributeName, NSWritingDirectionAttributeName, NSVerticalGlyphFormAttributeName, NSUnderlineColorAttributeName, NSAttachmentAttributeName, NSLinkAttributeName, NSBaselineOffsetAttributeName, nil]; NSAssert(![set containsObject:key], @"Invalid Key. This library doesn't support %@ yet.", key); #endif if (key == NSFontAttributeName) { UIFont *font = attributes[NSFontAttributeName]; [cssString appendFormat:@"font-family:'%@'; font-size: %0.fpx; ", font.fontName, roundf(font.pointSize)];; } else if (key == NSParagraphStyleAttributeName) { NSParagraphStyle *style = attributes[key]; NSString *alignment; switch (style.alignment) { case NSTextAlignmentCenter: alignment = @"center"; break; case NSTextAlignmentJustified: alignment = @"justify"; break; case NSTextAlignmentLeft: alignment = @"left"; break; case NSTextAlignmentNatural: alignment = @"initial"; break; case NSTextAlignmentRight: alignment = @"right"; break; default: alignment = @"left"; break; } [cssString appendFormat:@"line-height:%0.1f%%; text-align:%@;", style.lineHeightMultiple*100, alignment]; if (style.baseWritingDirection == NSWritingDirectionRightToLeft){ [cssString appendString:@"direction:rtl;"]; }else{ [cssString appendString:@"direction:ltr;"]; } } else if (key == NSForegroundColorAttributeName) { [cssString appendFormat:@"color:%@; ", [attributes[key] HTMLRGBString]]; } else if (key == NSBackgroundColorAttributeName ) { [cssString appendFormat:@"background-color:%@; ", [attributes[key] HTMLRGBString]];; } else if (key == NSShadowAttributeName) { NSShadow *shadow = attributes[NSShadowAttributeName]; [cssString appendFormat:@"text-shadow: %fpx %fpx %fpx %@; ", shadow.shadowOffset.width, shadow.shadowOffset.height, shadow.shadowBlurRadius, [shadow.shadowColor HTMLRGBString]]; } } [cssString appendString:@"}"]; return cssString; } - (NSString *)CSSStringFromAttributes { NSMutableString *string = [NSMutableString stringWithString:@""]; return string; } @end @implementation NSMutableDictionary (CSS) - (void)addAttributes:(NSDictionary *)attributes forHTMLAttribute:(QRHTMLAttribute *)tag flatten:(BOOL)flatten { if (!flatten) { [self setObject:attributes forKey:tag]; } else { [self setObject:[NSDictionary cssStringWithValues:attributes HTMLTag:tag] forKey:tag]; } } @end @implementation NSData (HTMLAdditions) - (NSData *)dataByReplacingOccurrencesOfData:(NSData *)data withData:(NSData *)replacementData { NSMutableData *mutableSelf = [self mutableCopy]; const void *replacementBytes = [replacementData bytes]; NSUInteger replacementBytesLength = [replacementData length]; NSRange rangeOfCharacters = [mutableSelf rangeOfData:data options:0 range:NSMakeRange(0, [mutableSelf length])]; while (rangeOfCharacters.location != NSNotFound) { [mutableSelf replaceBytesInRange:rangeOfCharacters withBytes:replacementBytes]; NSUInteger searchLocation = replacementBytesLength + rangeOfCharacters.location; rangeOfCharacters = [mutableSelf rangeOfData:data options:0 range:NSMakeRange(searchLocation, [mutableSelf length]-searchLocation)]; } return [NSData dataWithData:mutableSelf]; } @end ================================================ FILE: Pod/Classes/Extend/EUI/StringFormat.swift ================================================ // // StringFormat.swift // Pods // // Created by zhuchao on 15/10/15. // // import Foundation extension String{ var viewContentMode:UIViewContentMode{ var dict = Dictionary() dict["ScaleToFill"] = UIViewContentMode.ScaleToFill dict["ScaleAspectFit"] = UIViewContentMode.ScaleAspectFit dict["ScaleAspectFill"] = UIViewContentMode.ScaleAspectFill dict["Redraw"] = UIViewContentMode.Redraw dict["Center"] = UIViewContentMode.Center dict["Top"] = UIViewContentMode.Top dict["Bottom"] = UIViewContentMode.Bottom dict["Left"] = UIViewContentMode.Left dict["Right"] = UIViewContentMode.Right dict["TopLeft"] = UIViewContentMode.TopLeft dict["TopRight"] = UIViewContentMode.TopRight dict["BottomLeft"] = UIViewContentMode.BottomLeft dict["BottomRight"] = UIViewContentMode.BottomRight if let mode = dict[self.trim]{ return mode }else{ return UIViewContentMode.ScaleToFill } } var flexContentDirection:FLEXBOXContentDirection{ switch self.trim { case "ltr": return .LeftToRight case "rtl": return .RightToLeft case "inherit": return .Inherit default: return .LeftToRight } } var justifyContent:FLEXBOXJustification{ switch self.trim { case "center": return .Center case "flex-start": return .FlexStart case "flex-end": return .FlexEnd case "space-between": return .SpaceBetween case "space-around": return .SpaceAround default: return .FlexStart } } var alignItems:FLEXBOXAlignment{ switch self.trim { case "center": return .Center case "flex-start": return .FlexStart case "flex-end": return .FlexEnd case "stretch": return .Stretch case "auto": return .Auto default: return .Auto } } var flexDirection:FLEXBOXFlexDirection{ switch self.trim { case "column": return .Column case "row": return .Row case "row-reverse": return .RowReverse case "column-reverse": return .ColumnReverse default: return .Row } } var separatorStyle:UITableViewCellSeparatorStyle{ switch self.trim{ case "None" : return .None case "SingleLine": return .SingleLine case "SingleLineEtched": return .SingleLineEtched default: return .SingleLine } } var tableViewStyle:UITableViewStyle{ switch self.trim{ case "plain": return .Plain case "grouped": return .Grouped default: return .Plain } } var scrollViewIndicatorStyle:UIScrollViewIndicatorStyle{ switch self.trim{ case "white": return .White case "black": return .Black default: return .Default } } var textAlignment:NSTextAlignment { switch(self.trim){ case "center": return NSTextAlignment.Center case "left": return NSTextAlignment.Left case "right": return NSTextAlignment.Right case "justified": return NSTextAlignment.Justified case "natural": return NSTextAlignment.Natural default: return NSTextAlignment.Left } } var linkStyleDict:[NSObject:AnyObject]{ let linkArray = self.trimArrayBy(";") var dict = Dictionary() for str in linkArray { var strArray = str.trimArrayBy(":") if strArray.count == 2 { switch strArray[0] { case "color": dict[kCTForegroundColorAttributeName] = UIColor(CSS: strArray[1].trim) case "text-decoration": dict[NSUnderlineStyleAttributeName] = strArray[1].trim.underlineStyle.rawValue default : dict[NSUnderlineStyleAttributeName] = NSUnderlineStyle.StyleSingle.rawValue } } } return dict } var keyboardType:UIKeyboardType { switch self.lowercaseString { case "Default".lowercaseString: return UIKeyboardType.Default case "ASCIICapable".lowercaseString: return UIKeyboardType.ASCIICapable case "NumbersAndPunctuation".lowercaseString: return UIKeyboardType.NumbersAndPunctuation case "URL".lowercaseString: return UIKeyboardType.URL case "NumberPad".lowercaseString: return UIKeyboardType.NumberPad case "PhonePad".lowercaseString: return UIKeyboardType.PhonePad case "NamePhonePad".lowercaseString: return UIKeyboardType.NamePhonePad case "EmailAddress".lowercaseString: return UIKeyboardType.EmailAddress case "DecimalPad".lowercaseString: return UIKeyboardType.DecimalPad case "Twitter".lowercaseString: return UIKeyboardType.Twitter case "Twitter".lowercaseString: return UIKeyboardType.WebSearch default: return UIKeyboardType.Default } } var underlineStyle:NSUnderlineStyle{ switch self.lowercaseString { case "None".lowercaseString : return NSUnderlineStyle.StyleNone case "StyleSingle".lowercaseString : return NSUnderlineStyle.StyleSingle case "StyleThick".lowercaseString : return NSUnderlineStyle.StyleThick case "StyleDouble".lowercaseString : return NSUnderlineStyle.StyleDouble case "PatternDot".lowercaseString : return NSUnderlineStyle.PatternDot case "PatternDash".lowercaseString : return NSUnderlineStyle.PatternDash case "PatternDashDot".lowercaseString : return NSUnderlineStyle.PatternDashDot case "PatternDashDotDot".lowercaseString : return NSUnderlineStyle.PatternDashDotDot case "ByWord".lowercaseString : return NSUnderlineStyle.ByWord default : return NSUnderlineStyle.StyleSingle } } var controlEvent: UIControlEvents{ switch self.lowercaseString { case "TouchDown".lowercaseString : return UIControlEvents.TouchDown case "TouchDownRepeat".lowercaseString : return UIControlEvents.TouchDownRepeat case "TouchDragInside".lowercaseString : return UIControlEvents.TouchDragInside case "TouchDragOutside".lowercaseString : return UIControlEvents.TouchDragOutside case "TouchDragEnter".lowercaseString : return UIControlEvents.TouchDragEnter case "TouchDragExit".lowercaseString : return UIControlEvents.TouchDragExit case "TouchUpInside".lowercaseString : return UIControlEvents.TouchUpInside case "TouchUpOutside".lowercaseString : return UIControlEvents.TouchUpOutside case "ValueChanged".lowercaseString : return UIControlEvents.ValueChanged case "TouchCancel".lowercaseString : return UIControlEvents.TouchCancel case "EditingDidBegin".lowercaseString : return UIControlEvents.EditingDidBegin case "EditingChanged".lowercaseString : return UIControlEvents.EditingChanged case "EditingDidEnd".lowercaseString : return UIControlEvents.EditingDidEnd case "EditingDidEndOnExit".lowercaseString : return UIControlEvents.EditingDidEndOnExit case "AllTouchEvents".lowercaseString : return UIControlEvents.AllTouchEvents case "AllEditingEvents".lowercaseString : return UIControlEvents.AllEditingEvents case "ApplicationReserved".lowercaseString : return UIControlEvents.ApplicationReserved case "SystemReserved".lowercaseString : return UIControlEvents.SystemReserved case "AllEvents".lowercaseString : return UIControlEvents.AllEvents default : return UIControlEvents.TouchUpInside } } } ================================================ FILE: Pod/Classes/Extend/EUI/SwiftTryCatch.h ================================================ // // SwiftTryCatch.h // // Created by William Falcon on 10/10/14. // Copyright (c) 2014 William Falcon. All rights reserved. // /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import @import UIKit; @interface SwiftTryCatch : NSObject /** Provides try catch functionality for swift by wrapping around Objective-C */ + (void)try:(void(^)())try catch:(void(^)(NSException*exception))catch finally:(void(^)())finally; + (void)throwString:(NSString*)s; + (void)throwException:(NSException*)e; @end ================================================ FILE: Pod/Classes/Extend/EUI/SwiftTryCatch.m ================================================ // // SwiftTryCatch.h // // Created by William Falcon on 10/10/14. // Copyright (c) 2014 William Falcon. All rights reserved. // /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import "SwiftTryCatch.h" @implementation SwiftTryCatch /** Provides try catch functionality for swift by wrapping around Objective-C */ +(void)try:(void (^)())try catch:(void (^)(NSException *))catch finally:(void (^)())finally{ @try { try ? try() : nil; } @catch (NSException *exception) { catch ? catch(exception) : nil; } @finally { finally ? finally() : nil; } } + (void)throwString:(NSString*)s { @throw [NSException exceptionWithName:s reason:s userInfo:nil]; } + (void)throwException:(NSException*)e { @throw e; } @end ================================================ FILE: Pod/Classes/Extend/EUI/UIColor+HTMLColors.h ================================================ // // UIColor+HTMLColors.h // // Created by James Lawton on 12/9/12. // Copyright (c) 2012 James Lawton. All rights reserved. // #import /** * Extensions to read and write colors in the formats supported by CSS. * Emphasis has been given to parsing corrently formatted colors, rather * than rejecting technically invalid colors. */ @interface UIColor (HTMLColors) /** * Reads a color from a string containing hex, RGB, HSL or X11 named color. * Returns `nil` on failure. */ + (UIColor *)colorWithCSS:(NSString *)cssColor; /** * Reads a color from a string containing a hex color, of the form * "#FFFFFF" or "#FFF". * Returns `nil` on failure. */ + (UIColor *)colorWithHexString:(NSString *)hexColor; /** * Reads a color from a string containing an RGB color, of the form * "rgb(255, 255, 255)" or "rgba(255, 255, 255, 1.0)". Supports components * represented as percentages. * Returns `nil` on failure. */ + (UIColor *)colorWithRGBString:(NSString *)rgbColor; /** * Reads a color from a string containing an HSL color, of the form * "hsl(359, 100%, 100%)" or "hsla(359, 100%, 100%, 1.0)". * Returns `nil` on failure. */ + (UIColor *)colorWithHSLString:(NSString *)hslColor; /** * Reads a color from a string containing a W3C named color. * Returns `nil` on failure. */ + (UIColor *)colorWithW3CNamedColor:(NSString *)namedColor; /** * Returns a representation of this color as a hex string, of the form "#FFFFFF". * Alpha information is not represented. */ - (NSString *)hexStringValue; /** * Returns a representation of this color as an RGB string, of the form * "rgb(255, 255, 255)" or "rgba(255, 255, 255, 1.0)". * Returns `nil` on failure. */ - (NSString *)rgbStringValue; /** * Returns a representation of this color as an RGB string, of the form * "hsl(359, 100%, 100%)" or "hsla(359, 100%, 100%, 1.0)". * Returns `nil` on failure. */ - (NSString *)hslStringValue; /** * FOR DEBUGGING - All the supported W3C color names. */ + (NSArray *)W3CColorNames; @end /** * Extensions to scan colors in the formats supported by CSS. */ @interface NSScanner (HTMLColors) /** * Scan a color hex, RGB, HSL or X11 named color. */ - (BOOL)scanCSSColor:(UIColor **)color; /** * Scan an RGB color ("rgb(255, 255, 255)", "rgba(255, 255, 255, 1.0)"). */ - (BOOL)scanRGBColor:(UIColor **)color; /** * Scan an HSL color ("hsl(359, 100%, 100%)", "hsla(359, 100%, 100%, 1.0)"). */ - (BOOL)scanHSLColor:(UIColor **)color; /** * Scan a hex color ("#FFFFFF", "#FFF"). */ - (BOOL)scanHexColor:(UIColor **)color; /** * Scan a CSS3/SVG named color. These are similar to the X11 named colors. * See: http://www.w3.org/TR/css3-color/#svg-color */ - (BOOL)scanW3CNamedColor:(UIColor **)color; @end ================================================ FILE: Pod/Classes/Extend/EUI/UIColor+HTMLColors.m ================================================ // // UIColor+HTMLColors.m // // Created by James Lawton on 12/9/12. // Copyright (c) 2012 James Lawton. All rights reserved. // #import "UIColor+HTMLColors.h" typedef struct { CGFloat a, b, c; } CMRFloatTriple; typedef struct { CGFloat a, b, c, d; } CMRFloatQuad; // CSS uses HSL, but we have to specify UIColor as HSB static inline CMRFloatTriple HSB2HSL(CGFloat hue, CGFloat saturation, CGFloat brightness); static inline CMRFloatTriple HSL2HSB(CGFloat hue, CGFloat saturation, CGFloat lightness); static NSArray *CMRW3CColorNames(void); static NSDictionary *CMRW3CNamedColors(void); @implementation UIColor (HTMLColors) #pragma mark - Reading + (UIColor *)colorWithCSS:(NSString *)cssColor { UIColor *color = nil; NSScanner *scanner = [NSScanner scannerWithString:cssColor]; [scanner scanCSSColor:&color]; return (scanner.isAtEnd) ? color : nil; } + (UIColor *)colorWithHexString:(NSString *)hexColor { UIColor *color = nil; NSScanner *scanner = [NSScanner scannerWithString:hexColor]; [scanner scanHexColor:&color]; return (scanner.isAtEnd) ? color : nil; } + (UIColor *)colorWithRGBString:(NSString *)rgbColor { UIColor *color = nil; NSScanner *scanner = [NSScanner scannerWithString:rgbColor]; [scanner scanRGBColor:&color]; return (scanner.isAtEnd) ? color : nil; } + (UIColor *)colorWithHSLString:(NSString *)hslColor { UIColor *color = nil; NSScanner *scanner = [NSScanner scannerWithString:hslColor]; [scanner scanHSLColor:&color]; return (scanner.isAtEnd) ? color : nil; } + (UIColor *)colorWithW3CNamedColor:(NSString *)namedColor { UIColor *color = nil; NSScanner *scanner = [NSScanner scannerWithString:namedColor]; [scanner scanW3CNamedColor:&color]; return (scanner.isAtEnd) ? color : nil; } #pragma mark - Writing static inline unsigned ToByte(CGFloat f) { f = MAX(0, MIN(f, 1)); // Clamp return (unsigned)round(f * 255); } - (NSString *)hexStringValue { NSString *hex = nil; CGFloat red, green, blue, alpha; if ([self cmr_getRed:&red green:&green blue:&blue alpha:&alpha]) { hex = [NSString stringWithFormat:@"#%02X%02X%02X", ToByte(red), ToByte(green), ToByte(blue)]; } return hex; } - (NSString *)rgbStringValue { NSString *rgb = nil; CGFloat red, green, blue, alpha; if ([self cmr_getRed:&red green:&green blue:&blue alpha:&alpha]) { if (alpha == 1.0) { rgb = [NSString stringWithFormat:@"rgb(%u, %u, %u)", ToByte(red), ToByte(green), ToByte(blue)]; } else { rgb = [NSString stringWithFormat:@"rgba(%u, %u, %u, %g)", ToByte(red), ToByte(green), ToByte(blue), alpha]; } } return rgb; } static inline unsigned ToDeg(CGFloat f) { return (unsigned)round(f * 360) % 360; } static inline unsigned ToPercentage(CGFloat f) { f = MAX(0, MIN(f, 1)); // Clamp return (unsigned)round(f * 100); } - (NSString *)hslStringValue { NSString *hsl = nil; CGFloat hue, saturation, brightness, alpha; if ([self cmr_getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]) { CMRFloatTriple hslVal = HSB2HSL(hue, saturation, brightness); if (alpha == 1.0) { hsl = [NSString stringWithFormat:@"hsl(%u, %u%%, %u%%)", ToDeg(hslVal.a), ToPercentage(hslVal.b), ToPercentage(hslVal.c)]; } else { hsl = [NSString stringWithFormat:@"hsla(%u, %u%%, %u%%, %g)", ToDeg(hslVal.a), ToPercentage(hslVal.b), ToPercentage(hslVal.c), alpha]; } } return hsl; } // Fix up getting color components - (BOOL)cmr_getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha { if ([self getRed:red green:green blue:blue alpha:alpha]) { return YES; } CGFloat white; if ([self getWhite:&white alpha:alpha]) { if (red) *red = white; if (green) *green = white; if (blue) *blue = white; return YES; } return NO; } - (BOOL)cmr_getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha { if ([self getHue:hue saturation:saturation brightness:brightness alpha:alpha]) { return YES; } CGFloat white; if ([self getWhite:&white alpha:alpha]) { if (hue) *hue = 0; if (saturation) *saturation = 0; if (brightness) *brightness = white; return YES; } return NO; } + (NSArray *)W3CColorNames { return [[CMRW3CNamedColors() allKeys] sortedArrayUsingSelector:@selector(compare:)]; } @end @implementation NSScanner (HTMLColors) - (BOOL)scanCSSColor:(UIColor **)color { return [self scanHexColor:color] || [self scanRGBColor:color] || [self scanHSLColor:color] || [self cmr_scanTransparent:color] || [self scanW3CNamedColor:color]; } - (BOOL)scanRGBColor:(UIColor **)color { return [self cmr_caseInsensitiveWithCleanup:^BOOL{ if ([self scanString:@"rgba" intoString:NULL]) { CMRFloatQuad scale = {1.0/255.0, 1.0/255.0, 1.0/255.0, 1.0}; CMRFloatQuad q; if ([self cmr_scanFloatQuad:&q scale:scale]) { if (color) { *color = [UIColor colorWithRed:q.a green:q.b blue:q.c alpha:q.d]; } return YES; } } else if ([self scanString:@"rgb" intoString:NULL]) { CMRFloatTriple scale = {1.0/255.0, 1.0/255.0, 1.0/255.0}; CMRFloatTriple t; if ([self cmr_scanFloatTriple:&t scale:scale]) { if (color) { *color = [UIColor colorWithRed:t.a green:t.b blue:t.c alpha:1.0]; } return YES; } } return NO; }]; } // Wrap hues in a circle, where [0,1] = [0°,360°] static inline CGFloat CMRNormHue(CGFloat hue) { return hue - floor(hue); } - (BOOL)scanHSLColor:(UIColor **)color { return [self cmr_caseInsensitiveWithCleanup:^BOOL{ if ([self scanString:@"hsla" intoString:NULL]) { CMRFloatQuad scale = {1.0/360.0, 1.0, 1.0, 1.0}; CMRFloatQuad q; if ([self cmr_scanFloatQuad:&q scale:scale]) { if (color) { CMRFloatTriple t = HSL2HSB(CMRNormHue(q.a), q.b, q.c); *color = [UIColor colorWithHue:t.a saturation:t.b brightness:t.c alpha:q.d]; } return YES; } } else if ([self scanString:@"hsl" intoString:NULL]) { CMRFloatTriple scale = {1.0/360.0, 1.0, 1.0}; CMRFloatTriple t; if ([self cmr_scanFloatTriple:&t scale:scale]) { if (color) { t = HSL2HSB(CMRNormHue(t.a), t.b, t.c); *color = [UIColor colorWithHue:t.a saturation:t.b brightness:t.c alpha:1.0]; } return YES; } } return NO; }]; } - (BOOL)scanHexColor:(UIColor **)color { return [self cmr_resetScanLocationOnFailure:^BOOL{ return [self scanString:@"#" intoString:NULL] && [self cmr_scanHexTriple:color]; }]; } - (BOOL)scanW3CNamedColor:(UIColor **)color { return [self cmr_caseInsensitiveWithCleanup:^BOOL{ NSArray *colorNames = CMRW3CColorNames(); NSDictionary *namedColors = CMRW3CNamedColors(); for (NSString *name in colorNames) { if ([self scanString:name intoString:NULL]) { if (color) { *color = [UIColor colorWithHexString:namedColors[name]]; } return YES; } } return NO; }]; } #pragma mark - Private - (void)cmr_withSkip:(NSCharacterSet *)chars run:(void (^)(void))block { NSCharacterSet *skipped = self.charactersToBeSkipped; self.charactersToBeSkipped = chars; block(); self.charactersToBeSkipped = skipped; } - (void)cmr_withNoSkip:(void (^)(void))block { NSCharacterSet *skipped = self.charactersToBeSkipped; self.charactersToBeSkipped = nil; block(); self.charactersToBeSkipped = skipped; } - (NSRange)cmr_rangeFromScanLocation { NSUInteger loc = self.scanLocation; NSUInteger len = self.string.length - loc; return NSMakeRange(loc, len); } - (void)cmr_skipCharactersInSet:(NSCharacterSet *)chars { [self cmr_withNoSkip:^{ [self scanCharactersFromSet:chars intoString:NULL]; }]; } - (void)cmr_skip { [self cmr_skipCharactersInSet:self.charactersToBeSkipped]; } - (BOOL)cmr_resetScanLocationOnFailure:(BOOL (^)(void))block { NSUInteger initialScanLocation = self.scanLocation; if (!block()) { self.scanLocation = initialScanLocation; return NO; } return YES; } - (BOOL)cmr_caseInsensitiveWithCleanup:(BOOL (^)(void))block { NSUInteger initialScanLocation = self.scanLocation; BOOL caseSensitive = self.caseSensitive; self.caseSensitive = NO; BOOL success = block(); if (!success) { self.scanLocation = initialScanLocation; } self.caseSensitive = caseSensitive; return success; } // Scan, but only so far - (NSRange)cmr_scanCharactersInSet:(NSCharacterSet *)chars maxLength:(NSUInteger)maxLength intoString:(NSString **)outString { NSRange range = [self cmr_rangeFromScanLocation]; range.length = MIN(range.length, maxLength); NSUInteger len; for (len = 0; len < range.length; ++len) { if (![chars characterIsMember:[self.string characterAtIndex:(range.location + len)]]) { break; } } NSRange charRange = NSMakeRange(range.location, len); if (outString) { *outString = [self.string substringWithRange:charRange]; } self.scanLocation = charRange.location + charRange.length; return charRange; } // Hex characters static NSCharacterSet *CMRHexCharacters() { static NSCharacterSet *hexChars; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ hexChars = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"]; }); return hexChars; } // We know we've got hex already, so assume this works static NSUInteger CMRParseHex(NSString *str, BOOL repeated) { unsigned int ans = 0; if (repeated) { str = [NSString stringWithFormat:@"%@%@", str, str]; } NSScanner *scanner = [NSScanner scannerWithString:str]; [scanner scanHexInt:&ans]; return ans; } // Scan FFF or FFFFFF, doesn't reset scan location on failure - (BOOL)cmr_scanHexTriple:(UIColor **)color { NSString *hex = nil; NSRange range = [self cmr_scanCharactersInSet:CMRHexCharacters() maxLength:6 intoString:&hex]; CGFloat red, green, blue; if (hex.length == 6) { // Parse 2 chars per component red = CMRParseHex([hex substringWithRange:NSMakeRange(0, 2)], NO) / 255.0; green = CMRParseHex([hex substringWithRange:NSMakeRange(2, 2)], NO) / 255.0; blue = CMRParseHex([hex substringWithRange:NSMakeRange(4, 2)], NO) / 255.0; } else if (hex.length >= 3) { // Parse 1 char per component, but repeat it to calculate hex value red = CMRParseHex([hex substringWithRange:NSMakeRange(0, 1)], YES) / 255.0; green = CMRParseHex([hex substringWithRange:NSMakeRange(1, 1)], YES) / 255.0; blue = CMRParseHex([hex substringWithRange:NSMakeRange(2, 1)], YES) / 255.0; self.scanLocation = range.location + 3; } else { return NO; // Fail } if (color) { *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0]; } return YES; } // Scan "transparent" - (BOOL)cmr_scanTransparent:(UIColor **)color { return [self cmr_caseInsensitiveWithCleanup:^BOOL{ if ([self scanString:@"transparent" intoString:NULL]) { if (color) { *color = [UIColor colorWithWhite:0 alpha:0]; } return YES; } return NO; }]; } // Scan a float or percentage. Multiply float by `scale` if it was not a // percentage. - (BOOL)cmr_scanNum:(CGFloat *)value scale:(CGFloat)scale { float f = 0.0; if ([self scanFloat:&f]) { if ([self scanString:@"%" intoString:NULL]) { f *= 0.01; } else { f *= scale; } if (value) { *value = f; } return YES; } return NO; } // Scan a triple of numbers "(10, 10, 10)". If they are not percentages, multiply // by the corresponding `scale` component. - (BOOL)cmr_scanFloatTriple:(CMRFloatTriple *)triple scale:(CMRFloatTriple)scale { __block BOOL success = NO; __block CMRFloatTriple t; [self cmr_withSkip:[NSCharacterSet whitespaceAndNewlineCharacterSet] run:^{ success = [self scanString:@"(" intoString:NULL] && [self cmr_scanNum:&(t.a) scale:scale.a] && [self scanString:@"," intoString:NULL] && [self cmr_scanNum:&(t.b) scale:scale.b] && [self scanString:@"," intoString:NULL] && [self cmr_scanNum:&(t.c) scale:scale.c] && [self scanString:@")" intoString:NULL]; }]; if (triple) { *triple = t; } return success; } // Scan a quad of numbers "(10, 10, 10, 10)". If they are not percentages, // multiply by the corresponding `scale` component. - (BOOL)cmr_scanFloatQuad:(CMRFloatQuad *)quad scale:(CMRFloatQuad)scale { __block BOOL success = NO; __block CMRFloatQuad q; [self cmr_withSkip:[NSCharacterSet whitespaceAndNewlineCharacterSet] run:^{ success = [self scanString:@"(" intoString:NULL] && [self cmr_scanNum:&(q.a) scale:scale.a] && [self scanString:@"," intoString:NULL] && [self cmr_scanNum:&(q.b) scale:scale.b] && [self scanString:@"," intoString:NULL] && [self cmr_scanNum:&(q.c) scale:scale.c] && [self scanString:@"," intoString:NULL] && [self cmr_scanNum:&(q.d) scale:scale.d] && [self scanString:@")" intoString:NULL]; }]; if (quad) { *quad = q; } return success; } @end static inline CMRFloatTriple HSB2HSL(CGFloat hue, CGFloat saturation, CGFloat brightness) { CGFloat l = (2.0 - saturation) * brightness; saturation *= brightness; CGFloat satDiv = (l <= 1.0) ? l : (2.0 - l); if (satDiv) { saturation /= satDiv; } l *= 0.5; CMRFloatTriple hsl = { hue, saturation, l }; return hsl; } static inline CMRFloatTriple HSL2HSB(CGFloat hue, CGFloat saturation, CGFloat l) { l *= 2.0; CGFloat s = saturation * ((l <= 1.0) ? l : (2.0 - l)); CGFloat brightness = (l + s) * 0.5; if (s) { s = (2.0 * s) / (l + s); } CMRFloatTriple hsb = { hue, s, brightness }; return hsb; } // Color names, longest first static NSArray *CMRW3CColorNames() { static NSArray *colorNames; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ colorNames = [[CMRW3CNamedColors() allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *k1, NSString *k2) { NSInteger diff = k1.length - k2.length; if (!diff) { return NSOrderedSame; } else if (diff > 0) { return NSOrderedAscending; } else { return NSOrderedDescending; } }]; }); return colorNames; } // Color values as defined in CSS3 spec. // See: http://www.w3.org/TR/css3-color/#svg-color static NSDictionary *CMRW3CNamedColors() { static NSDictionary *namedColors; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ namedColors = @{ @"AliceBlue" : @"#F0F8FF", @"AntiqueWhite" : @"#FAEBD7", @"Aqua" : @"#00FFFF", @"Aquamarine" : @"#7FFFD4", @"Azure" : @"#F0FFFF", @"Beige" : @"#F5F5DC", @"Bisque" : @"#FFE4C4", @"Black" : @"#000000", @"BlanchedAlmond" : @"#FFEBCD", @"Blue" : @"#0000FF", @"BlueViolet" : @"#8A2BE2", @"Brown" : @"#A52A2A", @"BurlyWood" : @"#DEB887", @"CadetBlue" : @"#5F9EA0", @"Chartreuse" : @"#7FFF00", @"Chocolate" : @"#D2691E", @"Coral" : @"#FF7F50", @"CornflowerBlue" : @"#6495ED", @"Cornsilk" : @"#FFF8DC", @"Crimson" : @"#DC143C", @"Cyan" : @"#00FFFF", @"DarkBlue" : @"#00008B", @"DarkCyan" : @"#008B8B", @"DarkGoldenRod" : @"#B8860B", @"DarkGray" : @"#A9A9A9", @"DarkGrey" : @"#A9A9A9", @"DarkGreen" : @"#006400", @"DarkKhaki" : @"#BDB76B", @"DarkMagenta" : @"#8B008B", @"DarkOliveGreen" : @"#556B2F", @"DarkOrange" : @"#FF8C00", @"DarkOrchid" : @"#9932CC", @"DarkRed" : @"#8B0000", @"DarkSalmon" : @"#E9967A", @"DarkSeaGreen" : @"#8FBC8F", @"DarkSlateBlue" : @"#483D8B", @"DarkSlateGray" : @"#2F4F4F", @"DarkSlateGrey" : @"#2F4F4F", @"DarkTurquoise" : @"#00CED1", @"DarkViolet" : @"#9400D3", @"DeepPink" : @"#FF1493", @"DeepSkyBlue" : @"#00BFFF", @"DimGray" : @"#696969", @"DimGrey" : @"#696969", @"DodgerBlue" : @"#1E90FF", @"FireBrick" : @"#B22222", @"FloralWhite" : @"#FFFAF0", @"ForestGreen" : @"#228B22", @"Fuchsia" : @"#FF00FF", @"Gainsboro" : @"#DCDCDC", @"GhostWhite" : @"#F8F8FF", @"Gold" : @"#FFD700", @"GoldenRod" : @"#DAA520", @"Gray" : @"#808080", @"Grey" : @"#808080", @"Green" : @"#008000", @"GreenYellow" : @"#ADFF2F", @"HoneyDew" : @"#F0FFF0", @"HotPink" : @"#FF69B4", @"IndianRed" : @"#CD5C5C", @"Indigo" : @"#4B0082", @"Ivory" : @"#FFFFF0", @"Khaki" : @"#F0E68C", @"Lavender" : @"#E6E6FA", @"LavenderBlush" : @"#FFF0F5", @"LawnGreen" : @"#7CFC00", @"LemonChiffon" : @"#FFFACD", @"LightBlue" : @"#ADD8E6", @"LightCoral" : @"#F08080", @"LightCyan" : @"#E0FFFF", @"LightGoldenRodYellow" : @"#FAFAD2", @"LightGray" : @"#D3D3D3", @"LightGrey" : @"#D3D3D3", @"LightGreen" : @"#90EE90", @"LightPink" : @"#FFB6C1", @"LightSalmon" : @"#FFA07A", @"LightSeaGreen" : @"#20B2AA", @"LightSkyBlue" : @"#87CEFA", @"LightSlateGray" : @"#778899", @"LightSlateGrey" : @"#778899", @"LightSteelBlue" : @"#B0C4DE", @"LightYellow" : @"#FFFFE0", @"Lime" : @"#00FF00", @"LimeGreen" : @"#32CD32", @"Linen" : @"#FAF0E6", @"Magenta" : @"#FF00FF", @"Maroon" : @"#800000", @"MediumAquaMarine" : @"#66CDAA", @"MediumBlue" : @"#0000CD", @"MediumOrchid" : @"#BA55D3", @"MediumPurple" : @"#9370DB", @"MediumSeaGreen" : @"#3CB371", @"MediumSlateBlue" : @"#7B68EE", @"MediumSpringGreen" : @"#00FA9A", @"MediumTurquoise" : @"#48D1CC", @"MediumVioletRed" : @"#C71585", @"MidnightBlue" : @"#191970", @"MintCream" : @"#F5FFFA", @"MistyRose" : @"#FFE4E1", @"Moccasin" : @"#FFE4B5", @"NavajoWhite" : @"#FFDEAD", @"Navy" : @"#000080", @"OldLace" : @"#FDF5E6", @"Olive" : @"#808000", @"OliveDrab" : @"#6B8E23", @"Orange" : @"#FFA500", @"OrangeRed" : @"#FF4500", @"Orchid" : @"#DA70D6", @"PaleGoldenRod" : @"#EEE8AA", @"PaleGreen" : @"#98FB98", @"PaleTurquoise" : @"#AFEEEE", @"PaleVioletRed" : @"#DB7093", @"PapayaWhip" : @"#FFEFD5", @"PeachPuff" : @"#FFDAB9", @"Peru" : @"#CD853F", @"Pink" : @"#FFC0CB", @"Plum" : @"#DDA0DD", @"PowderBlue" : @"#B0E0E6", @"Purple" : @"#800080", @"Red" : @"#FF0000", @"RosyBrown" : @"#BC8F8F", @"RoyalBlue" : @"#4169E1", @"SaddleBrown" : @"#8B4513", @"Salmon" : @"#FA8072", @"SandyBrown" : @"#F4A460", @"SeaGreen" : @"#2E8B57", @"SeaShell" : @"#FFF5EE", @"Sienna" : @"#A0522D", @"Silver" : @"#C0C0C0", @"SkyBlue" : @"#87CEEB", @"SlateBlue" : @"#6A5ACD", @"SlateGray" : @"#708090", @"SlateGrey" : @"#708090", @"Snow" : @"#FFFAFA", @"SpringGreen" : @"#00FF7F", @"SteelBlue" : @"#4682B4", @"Tan" : @"#D2B48C", @"Teal" : @"#008080", @"Thistle" : @"#D8BFD8", @"Tomato" : @"#FF6347", @"Turquoise" : @"#40E0D0", @"Violet" : @"#EE82EE", @"Wheat" : @"#F5DEB3", @"White" : @"#FFFFFF", @"WhiteSmoke" : @"#F5F5F5", @"Yellow" : @"#FFFF00", @"YellowGreen" : @"#9ACD32" }; }); return namedColors; } ================================================ FILE: Pod/Classes/Extend/FlexboxKit/FLEXBOXContainerView.h ================================================ // // FLEXBOXContainerView.h // FlexboxKit // // Created by Alex Usbergo on 10/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import @interface FLEXBOXContainerView : UIView @end ================================================ FILE: Pod/Classes/Extend/FlexboxKit/FLEXBOXContainerView.m ================================================ // // FLEXBOXContainerView.m // FlexboxKit // // Created by Alex Usbergo on 10/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import "FLEXBOXContainerView.h" #import "UIView+FLEXBOX.h" @implementation FLEXBOXContainerView - (void)layoutSubviews { //default flexbox container attributes self.flex = self.flex < FLT_EPSILON ? 1 : self.flex; self.flexContainer = YES; [super layoutSubviews]; [self flexLayoutSubviews]; } @end ================================================ FILE: Pod/Classes/Extend/FlexboxKit/FLEXBOXNode.h ================================================ // // FLEXBOXNode.h // FlexboxKit // // Created by Alex Usbergo on 09/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import "Layout.h" @import UIKit; typedef NS_ENUM(NSInteger, FLEXBOXFlexDirection) { FLEXBOXFlexDirectionColumn = CSS_FLEX_DIRECTION_COLUMN, FLEXBOXFlexDirectionRow = CSS_FLEX_DIRECTION_ROW, FLEXBOXFlexDirectionRowReverse = CSS_FLEX_DIRECTION_ROW_REVERSE, FLEXBOXFlexDirectionColumnReverse = CSS_FLEX_DIRECTION_COLUMN_REVERSE }; typedef NS_ENUM(NSInteger, FLEXBOXContentDirection) { FLEXBOXContentDirectionInherit = CSS_DIRECTION_INHERIT, FLEXBOXContentDirectionLeftToRight = CSS_DIRECTION_LTR, FLEXBOXContentDirectionRightToLeft = CSS_DIRECTION_RTL }; typedef NS_ENUM(NSInteger, FLEXBOXJustification) { FLEXBOXJustificationFlexStart = CSS_JUSTIFY_FLEX_START, FLEXBOXJustificationCenter = CSS_JUSTIFY_CENTER, FLEXBOXJustificationFlexEnd = CSS_JUSTIFY_FLEX_END, FLEXBOXJustificationSpaceBetween = CSS_JUSTIFY_SPACE_BETWEEN, FLEXBOXJustificationSpaceAround = CSS_JUSTIFY_SPACE_AROUND }; typedef NS_ENUM(NSInteger, FLEXBOXAlignment) { FLEXBOXAlignmentAuto = CSS_ALIGN_AUTO, FLEXBOXAlignmentFlexStart = CSS_ALIGN_FLEX_START, FLEXBOXAlignmentCenter = CSS_ALIGN_CENTER, FLEXBOXAlignmentFlexEnd = CSS_ALIGN_FLEX_END, FLEXBOXAlignmentStretch = CSS_ALIGN_STRETCH }; extern const CGFloat FLEXBOXUndefinedDimension; @interface FLEXBOXNode : NSObject @property (nonatomic, readonly, assign) css_node_t *node; @property (nonatomic, readonly, assign) CGRect frame; @property (nonatomic, copy) CGSize (^measureBlock)(CGFloat width); @property (nonatomic, copy) FLEXBOXNode *(^childrenAtIndexBlock)(NSUInteger i); @property (nonatomic, copy) NSUInteger (^childrenCountBlock)(void); /// Compute the layout for the node constrained to the width passed as argument /// @param maximumWidth The maximum width or FLEXBOXUndefinedDimension - (void)layoutConstrainedToMaximumWidth:(CGFloat)maximumWidth; // Style @property (nonatomic, assign) CGSize dimensions; @property (nonatomic, assign) CGSize minDimensions; @property (nonatomic, assign) CGSize maxDimensions; @property (nonatomic, assign) FLEXBOXFlexDirection flexDirection; @property (nonatomic, assign) UIEdgeInsets margin; @property (nonatomic, assign) UIEdgeInsets padding; @property (nonatomic, assign) BOOL flexWrap; @property (nonatomic, assign) FLEXBOXJustification justifyContent; @property (nonatomic, assign) FLEXBOXAlignment alignSelf; @property (nonatomic, assign) FLEXBOXAlignment alignItems; @property (nonatomic, assign) CGFloat flex; @property (nonatomic, assign) FLEXBOXContentDirection contentDirection; @end ================================================ FILE: Pod/Classes/Extend/FlexboxKit/FLEXBOXNode.m ================================================ // // FLEXBOXNode.m // FlexboxKit // // Created by Alex Usbergo on 09/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import "FLEXBOXNode.h" const CGFloat FLEXBOXUndefinedDimension = CSS_UNDEFINED; static bool FLEXBOX_alwaysDirty(void *context) { return YES; } static css_node_t *FLEXBOX_getChild(void *context, int i) { FLEXBOXNode *_self = (__bridge FLEXBOXNode*)context; FLEXBOXNode *child = _self.childrenAtIndexBlock(i); return child.node; } static css_dim_t FLEXBOX_measureNode(void *context, float width) { FLEXBOXNode *_self = (__bridge FLEXBOXNode*)context; CGSize size = _self.measureBlock(width); return (css_dim_t){ size.width, size.height }; } @implementation FLEXBOXNode #pragma mark - Lifecycle - (instancetype)init { if (self = [super init]) { //initialise the css_node_t _node = new_css_node(); _node->context = (__bridge void *)self; _node->is_dirty = FLEXBOX_alwaysDirty; _node->measure = FLEXBOX_measureNode; _node->get_child = FLEXBOX_getChild; _maxDimensions = CGSizeZero; _minDimensions = CGSizeZero; //defaults self.flexDirection = FLEXBOXFlexDirectionColumn; self.flexWrap = NO; self.alignItems = FLEXBOXAlignmentStretch; self.alignSelf = FLEXBOXAlignmentAuto; self.margin = UIEdgeInsetsZero; self.padding = UIEdgeInsetsZero; self.justifyContent = FLEXBOXJustificationFlexStart; self.flex = 0; self.contentDirection = FLEXBOXContentDirectionInherit; } return self; } - (void)dealloc { free_css_node(_node); } #pragma mark - Layout and Internals - (void)prepareForLayout { if (self.childrenAtIndexBlock == nil) return; NSAssert(self.childrenCountBlock, nil); NSUInteger count = self.childrenCountBlock(); // prepares the nodes for the layout recursively for (NSInteger i = 0; i < count; i++) { FLEXBOXNode *node = self.childrenAtIndexBlock(i); [node prepareForLayout]; } // Apparently we need to reset these before laying out, otherwise the layout // has some weird additive effect. self.node->layout.position[CSS_LEFT] = 0; self.node->layout.position[CSS_TOP] = 0; self.node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; self.node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; } - (void)layoutConstrainedToMaximumWidth:(CGFloat)maximumWidth { _node->children_count = (int)self.childrenCountBlock(); maximumWidth = fabs(maximumWidth - FLT_MAX) < FLT_EPSILON ? FLEXBOXUndefinedDimension : maximumWidth; [self prepareForLayout]; layoutNode(_node, maximumWidth, _node->style.direction); } - (CGRect)frame { return (CGRect) { .origin.x = self.node->layout.position[CSS_LEFT], .origin.y = self.node->layout.position[CSS_TOP], .size.width = self.node->layout.dimensions[CSS_WIDTH], .size.height = self.node->layout.dimensions[CSS_HEIGHT] }; } #pragma mark - Style - (void)setDimensions:(CGSize)size { _dimensions = size; _node->style.dimensions[CSS_WIDTH] = size.width; _node->style.dimensions[CSS_HEIGHT] = size.height; } - (void)setMinDimensions:(CGSize)size { _minDimensions = size; // _node->style.minDimensions[CSS_WIDTH] = size.width; // _node->style.minDimensions[CSS_HEIGHT] = size.height; } - (void)setMaxDimensions:(CGSize)size { _maxDimensions = size; // _node->style.maxDimensions[CSS_WIDTH] = size.width; // _node->style.maxDimensions[CSS_HEIGHT] = size.height; } - (void)setFlexDirection:(FLEXBOXFlexDirection)flexDirection { _flexDirection = flexDirection; _node->style.flex_direction = (int)flexDirection; } - (void)setMargin:(UIEdgeInsets)margin { _margin = margin; _node->style.margin[CSS_LEFT] = margin.left; _node->style.margin[CSS_TOP] = margin.top; _node->style.margin[CSS_RIGHT] = margin.right; _node->style.margin[CSS_BOTTOM] = margin.bottom; } - (void)setPadding:(UIEdgeInsets)padding { _padding = padding; _node->style.padding[CSS_LEFT] = padding.left; _node->style.padding[CSS_TOP] = padding.top; _node->style.padding[CSS_RIGHT] = padding.right; _node->style.padding[CSS_BOTTOM] = padding.bottom; } - (void)setFlex:(CGFloat)flex { _flex = flex; _node->style.flex = flex; } - (void)setFlexWrap:(BOOL)flexWrap { _flexWrap = flexWrap; _node->style.flex_wrap = flexWrap; } - (void)setJustifyContent:(FLEXBOXJustification)justifyContent { _justifyContent = justifyContent; _node->style.justify_content = (int)justifyContent; } - (void)setAlignItems:(FLEXBOXAlignment)alignItems { _alignItems = alignItems; _node->style.align_items = (int)alignItems; } - (void)setAlignSelf:(FLEXBOXAlignment)alignSelf { _alignSelf = alignSelf; _node->style.align_self = (int)alignSelf; } - (void)setContentDirection:(FLEXBOXContentDirection)contentDirection { _contentDirection = contentDirection; _node->style.direction = (int)contentDirection; } @end ================================================ FILE: Pod/Classes/Extend/FlexboxKit/FlexboxKit.h ================================================ // // FlexboxKit.h // FlexboxKit // // Created by Alex Usbergo on 09/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import #import "FLEXBOXNode.h" #import "UIView+FLEXBOX.h" #import "FLEXBOXContainerView.h" //! Project version number for FlexboxKit. FOUNDATION_EXPORT double FlexboxKitVersionNumber; //! Project version string for FlexboxKit. FOUNDATION_EXPORT const unsigned char FlexboxKitVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: Pod/Classes/Extend/FlexboxKit/Layout.c ================================================ /** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #include #include #include #include #include "Layout.h" #ifdef _MSC_VER #include #define isnan _isnan /* define fmaxf if < VC12 */ #if _MSC_VER < 1800 __forceinline const float fmaxf(const float a, const float b) { return (a > b) ? a : b; } #endif #endif bool isUndefined(float value) { return isnan(value); } static bool eq(float a, float b) { if (isUndefined(a)) { return isUndefined(b); } return fabs(a - b) < 0.0001; } void init_css_node(css_node_t *node) { node->style.align_items = CSS_ALIGN_STRETCH; node->style.align_content = CSS_ALIGN_FLEX_START; node->style.direction = CSS_DIRECTION_INHERIT; node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; // Some of the fields default to undefined and not 0 node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED; node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED; node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED; node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED; node->style.position[CSS_LEFT] = CSS_UNDEFINED; node->style.position[CSS_TOP] = CSS_UNDEFINED; node->style.position[CSS_RIGHT] = CSS_UNDEFINED; node->style.position[CSS_BOTTOM] = CSS_UNDEFINED; node->style.margin[CSS_START] = CSS_UNDEFINED; node->style.margin[CSS_END] = CSS_UNDEFINED; node->style.padding[CSS_START] = CSS_UNDEFINED; node->style.padding[CSS_END] = CSS_UNDEFINED; node->style.border[CSS_START] = CSS_UNDEFINED; node->style.border[CSS_END] = CSS_UNDEFINED; node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; // Such that the comparison is always going to be false node->layout.last_requested_dimensions[CSS_WIDTH] = -1; node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; node->layout.last_parent_max_width = -1; node->layout.last_direction = (css_direction_t)-1; node->layout.should_update = true; } css_node_t *new_css_node() { css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); init_css_node(node); return node; } void free_css_node(css_node_t *node) { free(node); } static void indent(int n) { for (int i = 0; i < n; ++i) { printf(" "); } } static void print_number_0(const char *str, float number) { if (!eq(number, 0)) { printf("%s: %g, ", str, number); } } static void print_number_nan(const char *str, float number) { if (!isnan(number)) { printf("%s: %g, ", str, number); } } static bool four_equal(float four[4]) { return eq(four[0], four[1]) && eq(four[0], four[2]) && eq(four[0], four[3]); } static void print_css_node_rec( css_node_t *node, css_print_options_t options, int level ) { indent(level); printf("{"); if (node->print) { node->print(node->context); } if (options & CSS_PRINT_LAYOUT) { printf("layout: {"); printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]); printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]); printf("top: %g, ", node->layout.position[CSS_TOP]); printf("left: %g", node->layout.position[CSS_LEFT]); printf("}, "); } if (options & CSS_PRINT_STYLE) { if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { printf("flexDirection: 'column', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { printf("flexDirection: 'columnReverse', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { printf("flexDirection: 'row', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { printf("flexDirection: 'rowReverse', "); } if (node->style.justify_content == CSS_JUSTIFY_CENTER) { printf("justifyContent: 'center', "); } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) { printf("justifyContent: 'flex-end', "); } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) { printf("justifyContent: 'space-around', "); } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) { printf("justifyContent: 'space-between', "); } if (node->style.align_items == CSS_ALIGN_CENTER) { printf("alignItems: 'center', "); } else if (node->style.align_items == CSS_ALIGN_FLEX_END) { printf("alignItems: 'flex-end', "); } else if (node->style.align_items == CSS_ALIGN_STRETCH) { printf("alignItems: 'stretch', "); } if (node->style.align_content == CSS_ALIGN_CENTER) { printf("alignContent: 'center', "); } else if (node->style.align_content == CSS_ALIGN_FLEX_END) { printf("alignContent: 'flex-end', "); } else if (node->style.align_content == CSS_ALIGN_STRETCH) { printf("alignContent: 'stretch', "); } if (node->style.align_self == CSS_ALIGN_FLEX_START) { printf("alignSelf: 'flex-start', "); } else if (node->style.align_self == CSS_ALIGN_CENTER) { printf("alignSelf: 'center', "); } else if (node->style.align_self == CSS_ALIGN_FLEX_END) { printf("alignSelf: 'flex-end', "); } else if (node->style.align_self == CSS_ALIGN_STRETCH) { printf("alignSelf: 'stretch', "); } print_number_nan("flex", node->style.flex); if (four_equal(node->style.margin)) { print_number_0("margin", node->style.margin[CSS_LEFT]); } else { print_number_0("marginLeft", node->style.margin[CSS_LEFT]); print_number_0("marginRight", node->style.margin[CSS_RIGHT]); print_number_0("marginTop", node->style.margin[CSS_TOP]); print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]); print_number_0("marginStart", node->style.margin[CSS_START]); print_number_0("marginEnd", node->style.margin[CSS_END]); } if (four_equal(node->style.padding)) { print_number_0("padding", node->style.margin[CSS_LEFT]); } else { print_number_0("paddingLeft", node->style.padding[CSS_LEFT]); print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); print_number_0("paddingTop", node->style.padding[CSS_TOP]); print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]); print_number_0("paddingStart", node->style.padding[CSS_START]); print_number_0("paddingEnd", node->style.padding[CSS_END]); } if (four_equal(node->style.border)) { print_number_0("borderWidth", node->style.border[CSS_LEFT]); } else { print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]); print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]); print_number_0("borderTopWidth", node->style.border[CSS_TOP]); print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]); print_number_0("borderStartWidth", node->style.border[CSS_START]); print_number_0("borderEndWidth", node->style.border[CSS_END]); } print_number_nan("width", node->style.dimensions[CSS_WIDTH]); print_number_nan("height", node->style.dimensions[CSS_HEIGHT]); if (node->style.position_type == CSS_POSITION_ABSOLUTE) { printf("position: 'absolute', "); } print_number_nan("left", node->style.position[CSS_LEFT]); print_number_nan("right", node->style.position[CSS_RIGHT]); print_number_nan("top", node->style.position[CSS_TOP]); print_number_nan("bottom", node->style.position[CSS_BOTTOM]); } if (options & CSS_PRINT_CHILDREN && node->children_count > 0) { printf("children: [\n"); for (int i = 0; i < node->children_count; ++i) { print_css_node_rec(node->get_child(node->context, i), options, level + 1); } indent(level); printf("]},\n"); } else { printf("},\n"); } } void print_css_node(css_node_t *node, css_print_options_t options) { print_css_node_rec(node, options, 0); } static css_position_t leading[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT }; static css_position_t trailing[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP, /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT, /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT }; static css_position_t pos[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT }; static css_dimension_t dim[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT, /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH, /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH }; static bool isRowDirection(css_flex_direction_t flex_direction) { return flex_direction == CSS_FLEX_DIRECTION_ROW || flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE; } static bool isColumnDirection(css_flex_direction_t flex_direction) { return flex_direction == CSS_FLEX_DIRECTION_COLUMN || flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; } static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { return node->style.margin[CSS_START]; } return node->style.margin[leading[axis]]; } static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { return node->style.margin[CSS_END]; } return node->style.margin[trailing[axis]]; } static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_START]) && node->style.padding[CSS_START] >= 0) { return node->style.padding[CSS_START]; } if (node->style.padding[leading[axis]] >= 0) { return node->style.padding[leading[axis]]; } return 0; } static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_END]) && node->style.padding[CSS_END] >= 0) { return node->style.padding[CSS_END]; } if (node->style.padding[trailing[axis]] >= 0) { return node->style.padding[trailing[axis]]; } return 0; } static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_START]) && node->style.border[CSS_START] >= 0) { return node->style.border[CSS_START]; } if (node->style.border[leading[axis]] >= 0) { return node->style.border[leading[axis]]; } return 0; } static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_END]) && node->style.border[CSS_END] >= 0) { return node->style.border[CSS_END]; } if (node->style.border[trailing[axis]] >= 0) { return node->style.border[trailing[axis]]; } return 0; } static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); } static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); } static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); } static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); } static css_position_type_t getPositionType(css_node_t *node) { return node->style.position_type; } static css_justify_t getJustifyContent(css_node_t *node) { return node->style.justify_content; } static css_align_t getAlignContent(css_node_t *node) { return node->style.align_content; } static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { if (child->style.align_self != CSS_ALIGN_AUTO) { return child->style.align_self; } return node->style.align_items; } static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { css_direction_t direction = node->style.direction; if (direction == CSS_DIRECTION_INHERIT) { direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR; } return direction; } static css_flex_direction_t getFlexDirection(css_node_t *node) { return node->style.flex_direction; } static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) { if (direction == CSS_DIRECTION_RTL) { if (flex_direction == CSS_FLEX_DIRECTION_ROW) { return CSS_FLEX_DIRECTION_ROW_REVERSE; } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { return CSS_FLEX_DIRECTION_ROW; } } return flex_direction; } static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) { if (isColumnDirection(flex_direction)) { return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); } else { return CSS_FLEX_DIRECTION_COLUMN; } } static float getFlex(css_node_t *node) { return node->style.flex; } static bool isFlex(css_node_t *node) { return ( getPositionType(node) == CSS_POSITION_RELATIVE && getFlex(node) > 0 ); } static bool isFlexWrap(css_node_t *node) { return node->style.flex_wrap == CSS_WRAP; } static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { return node->layout.dimensions[dim[axis]] + getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) { float value = node->style.dimensions[dim[axis]]; return !isUndefined(value) && value > 0.0; } static bool isPosDefined(css_node_t *node, css_position_t position) { return !isUndefined(node->style.position[position]); } static bool isMeasureDefined(css_node_t *node) { return node->measure; } static float getPosition(css_node_t *node, css_position_t position) { float result = node->style.position[position]; if (!isUndefined(result)) { return result; } return 0; } static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { float min = CSS_UNDEFINED; float max = CSS_UNDEFINED; if (isColumnDirection(axis)) { min = node->style.minDimensions[CSS_HEIGHT]; max = node->style.maxDimensions[CSS_HEIGHT]; } else if (isRowDirection(axis)) { min = node->style.minDimensions[CSS_WIDTH]; max = node->style.maxDimensions[CSS_WIDTH]; } float boundValue = value; if (!isUndefined(max) && max >= 0.0 && boundValue > max) { boundValue = max; } if (!isUndefined(min) && min >= 0.0 && boundValue < min) { boundValue = min; } return boundValue; } // When the user specifically sets a value for width or height static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { // The parent already computed us a width or height. We just skip it if (!isUndefined(node->layout.dimensions[dim[axis]])) { return; } // We only run if there's a width or height defined if (!isDimDefined(node, axis)) { return; } // The dimensions can never be smaller than the padding and border node->layout.dimensions[dim[axis]] = fmaxf( boundAxis(node, axis, node->style.dimensions[dim[axis]]), getPaddingAndBorderAxis(node, axis) ); } static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; } // If both left and right are defined, then use left. Otherwise return // +left or -right depending on which is defined. static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { float lead = node->style.position[leading[axis]]; if (!isUndefined(lead)) { return lead; } return -getPosition(node, trailing[axis]); } static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { /** START_GENERATED **/ css_direction_t direction = resolveDirection(node, parentDirection); css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); // Handle width and height style attributes setDimensionFromStyle(node, mainAxis); setDimensionFromStyle(node, crossAxis); // Set the resolved resolution in the node's layout node->layout.direction = direction; // The position is set by the parent, but we need to complete it with a // delta composed of the margin and left/top/right/bottom node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + getRelativePosition(node, mainAxis); node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + getRelativePosition(node, mainAxis); node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + getRelativePosition(node, crossAxis); node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + getRelativePosition(node, crossAxis); if (isMeasureDefined(node)) { float width = CSS_UNDEFINED; if (isDimDefined(node, resolvedRowAxis)) { width = node->style.dimensions[CSS_WIDTH]; } else if (!isUndefined(node->layout.dimensions[dim[resolvedRowAxis]])) { width = node->layout.dimensions[dim[resolvedRowAxis]]; } else { width = parentMaxWidth - getMarginAxis(node, resolvedRowAxis); } width -= getPaddingAndBorderAxis(node, resolvedRowAxis); // We only need to give a dimension for the text if we haven't got any // for it computed yet. It can either be from the style attribute or because // the element is flexible. bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) && isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]); bool isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); // Let's not measure the text if we already know both dimensions if (isRowUndefined || isColumnUndefined) { css_dim_t measureDim = node->measure( node->context, width ); if (isRowUndefined) { node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + getPaddingAndBorderAxis(node, resolvedRowAxis); } if (isColumnUndefined) { node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); } } if (node->children_count == 0) { return; } } int i; int ii; css_node_t* child; css_flex_direction_t axis; // Pre-fill some dimensions straight from the parent for (i = 0; i < node->children_count; ++i) { child = node->get_child(node->context, i); // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass if (getAlignItem(node, child) == CSS_ALIGN_STRETCH && getPositionType(child) == CSS_POSITION_RELATIVE && !isUndefined(node->layout.dimensions[dim[crossAxis]]) && !isDimDefined(child, crossAxis)) { child->layout.dimensions[dim[crossAxis]] = fmaxf( boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - getPaddingAndBorderAxis(node, crossAxis) - getMarginAxis(child, crossAxis)), // You never want to go smaller than padding getPaddingAndBorderAxis(child, crossAxis) ); } else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both // left and right or top and bottom). for (ii = 0; ii < 2; ii++) { axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (!isUndefined(node->layout.dimensions[dim[axis]]) && !isDimDefined(child, axis) && isPosDefined(child, leading[axis]) && isPosDefined(child, trailing[axis])) { child->layout.dimensions[dim[axis]] = fmaxf( boundAxis(child, axis, node->layout.dimensions[dim[axis]] - getPaddingAndBorderAxis(node, axis) - getMarginAxis(child, axis) - getPosition(child, leading[axis]) - getPosition(child, trailing[axis])), // You never want to go smaller than padding getPaddingAndBorderAxis(child, axis) ); } } } } float definedMainDim = CSS_UNDEFINED; if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { definedMainDim = node->layout.dimensions[dim[mainAxis]] - getPaddingAndBorderAxis(node, mainAxis); } // We want to execute the next two loops one per line with flex-wrap int startLine = 0; int endLine = 0; // int nextOffset = 0; int alreadyComputedNextLayout = 0; // We aggregate the total dimensions of the container in those two variables float linesCrossDim = 0; float linesMainDim = 0; int linesCount = 0; while (endLine < node->children_count) { // Layout non flexible children and count children by type // mainContentDim is accumulation of the dimensions and margin of all the // non flexible children. This will be used in order to either set the // dimensions of the node if none already exist, or to compute the // remaining space left for the flexible children. float mainContentDim = 0; // There are three kind of children, non flexible, flexible and absolute. // We need to know how many there are in order to distribute the space. int flexibleChildrenCount = 0; float totalFlexible = 0; int nonFlexibleChildrenCount = 0; float maxWidth; for (i = startLine; i < node->children_count; ++i) { child = node->get_child(node->context, i); float nextContentDim = 0; // It only makes sense to consider a child flexible if we have a computed // dimension for the node-> if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) { flexibleChildrenCount++; totalFlexible += getFlex(child); // Even if we don't know its exact size yet, we already know the padding, // border and margin. We'll use this partial information, which represents // the smallest possible size for the child, to compute the remaining // available space. nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + getMarginAxis(child, mainAxis); } else { maxWidth = CSS_UNDEFINED; if (!isRowDirection(mainAxis)) { maxWidth = parentMaxWidth - getMarginAxis(node, resolvedRowAxis) - getPaddingAndBorderAxis(node, resolvedRowAxis); if (isDimDefined(node, resolvedRowAxis)) { maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - getPaddingAndBorderAxis(node, resolvedRowAxis); } } // This is the main recursive call. We layout non flexible children. if (alreadyComputedNextLayout == 0) { layoutNode(child, maxWidth, direction); } // Absolute positioned elements do not take part of the layout, so we // don't use them to compute mainContentDim if (getPositionType(child) == CSS_POSITION_RELATIVE) { nonFlexibleChildrenCount++; // At this point we know the final size and margin of the element. nextContentDim = getDimWithMargin(child, mainAxis); } } // The element we are about to add would make us go to the next line if (isFlexWrap(node) && !isUndefined(node->layout.dimensions[dim[mainAxis]]) && mainContentDim + nextContentDim > definedMainDim && // If there's only one element, then it's bigger than the content // and needs its own line i != startLine) { nonFlexibleChildrenCount--; alreadyComputedNextLayout = 1; break; } alreadyComputedNextLayout = 0; mainContentDim += nextContentDim; endLine = i + 1; } // Layout flexible children and allocate empty space // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element // and the space between each two elements. float leadingMainDim = 0; float betweenMainDim = 0; // The remaining available space that needs to be allocated float remainingMainDim = 0; if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { remainingMainDim = definedMainDim - mainContentDim; } else { remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; } // If there are flexible children in the mix, they are going to fill the // remaining space if (flexibleChildrenCount != 0) { float flexibleMainDim = remainingMainDim / totalFlexible; float baseMainDim; float boundMainDim; // Iterate over every child in the axis. If the flex share of remaining // space doesn't meet min/max bounds, remove this child from flex // calculations. for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); if (isFlex(child)) { baseMainDim = flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis); boundMainDim = boundAxis(child, mainAxis, baseMainDim); if (baseMainDim != boundMainDim) { remainingMainDim -= boundMainDim; totalFlexible -= getFlex(child); } } } flexibleMainDim = remainingMainDim / totalFlexible; // The non flexible children can overflow the container, in this case // we should just assume that there is no space available. if (flexibleMainDim < 0) { flexibleMainDim = 0; } // We iterate over the full array and only apply the action on flexible // children. This is faster than actually allocating a new array that // contains only flexible children. for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); if (isFlex(child)) { // At this point we know the final size of the element in the main // dimension child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis, flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis) ); maxWidth = CSS_UNDEFINED; if (isDimDefined(node, resolvedRowAxis)) { maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - getPaddingAndBorderAxis(node, resolvedRowAxis); } else if (!isRowDirection(mainAxis)) { maxWidth = parentMaxWidth - getMarginAxis(node, resolvedRowAxis) - getPaddingAndBorderAxis(node, resolvedRowAxis); } // And we recursively call the layout algorithm for this child layoutNode(child, maxWidth, direction); } } // We use justifyContent to figure out how to allocate the remaining // space available } else { css_justify_t justifyContent = getJustifyContent(node); if (justifyContent == CSS_JUSTIFY_CENTER) { leadingMainDim = remainingMainDim / 2; } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { leadingMainDim = remainingMainDim; } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) { remainingMainDim = fmaxf(remainingMainDim, 0); if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { betweenMainDim = remainingMainDim / (flexibleChildrenCount + nonFlexibleChildrenCount - 1); } else { betweenMainDim = 0; } } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) { // Space on the edges is half of the space between elements betweenMainDim = remainingMainDim / (flexibleChildrenCount + nonFlexibleChildrenCount); leadingMainDim = betweenMainDim / 2; } } // Position elements in the main axis and compute dimensions // At this point, all the children have their dimensions set. We need to // find their position. In order to do that, we accumulate data in // variables that are also useful to compute the total dimensions of the // container! float crossDim = 0; float mainDim = leadingMainDim + getLeadingPaddingAndBorder(node, mainAxis); for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); child->line_index = linesCount; if (getPositionType(child) == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { // In case the child is position absolute and has left/top being // defined, we override the position to whatever the user said // (and margin/border). child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + getLeadingBorder(node, mainAxis) + getLeadingMargin(child, mainAxis); } else { // If the child is position absolute (without top/left) or relative, // we put it at the current accumulated offset. child->layout.position[pos[mainAxis]] += mainDim; // Define the trailing position accordingly. if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { setTrailingPosition(node, child, mainAxis); } } // Now that we placed the element, we need to update the variables // We only need to do that for relative elements. Absolute elements // do not take part in that phase. if (getPositionType(child) == CSS_POSITION_RELATIVE) { // The main dimension is the sum of all the elements dimension plus // the spacing. mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); // The cross dimension is the max of the elements dimension since there // can only be one element in that cross dimension. crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); } } float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { containerCrossAxis = fmaxf( // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)), getPaddingAndBorderAxis(node, crossAxis) ); } // Position elements in the cross axis for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[crossAxis])) { // In case the child is absolutely positionned and has a // top/left/bottom/right being set, we override all the previously // computed positions to set it correctly. child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + getLeadingBorder(node, crossAxis) + getLeadingMargin(child, crossAxis); } else { float leadingCrossDim = getLeadingPaddingAndBorder(node, crossAxis); // For a relative children, we're either using alignItems (parent) or // alignSelf (child) in order to determine the position in the cross axis if (getPositionType(child) == CSS_POSITION_RELATIVE) { css_align_t alignItem = getAlignItem(node, child); if (alignItem == CSS_ALIGN_STRETCH) { // You can only stretch if the dimension has not already been set // previously. if (!isDimDefined(child, crossAxis)) { child->layout.dimensions[dim[crossAxis]] = fmaxf( boundAxis(child, crossAxis, containerCrossAxis - getPaddingAndBorderAxis(node, crossAxis) - getMarginAxis(child, crossAxis)), // You never want to go smaller than padding getPaddingAndBorderAxis(child, crossAxis) ); } } else if (alignItem != CSS_ALIGN_FLEX_START) { // The remaining space between the parent dimensions+padding and child // dimensions+margin. float remainingCrossDim = containerCrossAxis - getPaddingAndBorderAxis(node, crossAxis) - getDimWithMargin(child, crossAxis); if (alignItem == CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; } else { // CSS_ALIGN_FLEX_END leadingCrossDim += remainingCrossDim; } } } // And we apply the position child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; // Define the trailing position accordingly. if (!isUndefined(node->layout.dimensions[dim[crossAxis]])) { setTrailingPosition(node, child, crossAxis); } } } linesCrossDim += crossDim; linesMainDim = fmaxf(linesMainDim, mainDim); linesCount += 1; startLine = endLine; } // // // Note(prenaux): More than one line, we need to layout the crossAxis // according to alignContent. // // Note that we could probably remove and handle the one line case // here too, but for the moment this is safer since it won't interfere with // previously working code. // // See specs: // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm // section 9.4 // if (linesCount > 1 && !isUndefined(node->layout.dimensions[dim[crossAxis]])) { float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - getPaddingAndBorderAxis(node, crossAxis); float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; float crossDimLead = 0; float currentLead = getLeadingPaddingAndBorder(node, crossAxis); css_align_t alignContent = getAlignContent(node); if (alignContent == CSS_ALIGN_FLEX_END) { currentLead += remainingAlignContentDim; } else if (alignContent == CSS_ALIGN_CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent == CSS_ALIGN_STRETCH) { if (nodeCrossAxisInnerSize > linesCrossDim) { crossDimLead = (remainingAlignContentDim / linesCount); } } int endIndex = 0; for (i = 0; i < linesCount; ++i) { int startIndex = endIndex; // compute the line's height and find the endIndex float lineHeight = 0; for (ii = startIndex; ii < node->children_count; ++ii) { child = node->get_child(node->context, ii); if (getPositionType(child) != CSS_POSITION_RELATIVE) { continue; } if (child->line_index != i) { break; } if (!isUndefined(child->layout.dimensions[dim[crossAxis]])) { lineHeight = fmaxf( lineHeight, child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) ); } } endIndex = ii; lineHeight += crossDimLead; for (ii = startIndex; ii < endIndex; ++ii) { child = node->get_child(node->context, ii); if (getPositionType(child) != CSS_POSITION_RELATIVE) { continue; } css_align_t alignContentAlignItem = getAlignItem(node, child); if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { float childHeight = child->layout.dimensions[dim[crossAxis]]; child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); // TODO(prenaux): Correctly set the height of items with undefined // (auto) crossAxis dimension. } } currentLead += lineHeight; } } bool needsMainTrailingPos = false; bool needsCrossTrailingPos = false; // If the user didn't specify a width or height, and it has not been set // by the container, then we set it via the children. if (isUndefined(node->layout.dimensions[dim[mainAxis]])) { node->layout.dimensions[dim[mainAxis]] = fmaxf( // We're missing the last padding at this point to get the final // dimension boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), // We can never assign a width smaller than the padding and borders getPaddingAndBorderAxis(node, mainAxis) ); needsMainTrailingPos = true; } if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { node->layout.dimensions[dim[crossAxis]] = fmaxf( // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)), getPaddingAndBorderAxis(node, crossAxis) ); needsCrossTrailingPos = true; } // Set trailing position if necessary if (needsMainTrailingPos || needsCrossTrailingPos) { for (i = 0; i < node->children_count; ++i) { child = node->get_child(node->context, i); if (needsMainTrailingPos) { setTrailingPosition(node, child, mainAxis); } if (needsCrossTrailingPos) { setTrailingPosition(node, child, crossAxis); } } } // Calculate dimensions for absolutely positioned elements for (i = 0; i < node->children_count; ++i) { child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both // left and right or top and bottom). for (ii = 0; ii < 2; ii++) { axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (!isUndefined(node->layout.dimensions[dim[axis]]) && !isDimDefined(child, axis) && isPosDefined(child, leading[axis]) && isPosDefined(child, trailing[axis])) { child->layout.dimensions[dim[axis]] = fmaxf( boundAxis(child, axis, node->layout.dimensions[dim[axis]] - getBorderAxis(node, axis) - getMarginAxis(child, axis) - getPosition(child, leading[axis]) - getPosition(child, trailing[axis]) ), // You never want to go smaller than padding getPaddingAndBorderAxis(child, axis) ); } } for (ii = 0; ii < 2; ii++) { axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; if (isPosDefined(child, trailing[axis]) && !isPosDefined(child, leading[axis])) { child->layout.position[leading[axis]] = node->layout.dimensions[dim[axis]] - child->layout.dimensions[dim[axis]] - getPosition(child, trailing[axis]); } } } } /** END_GENERATED **/ } void layoutNode(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { css_layout_t *layout = &node->layout; css_direction_t direction = node->style.direction; layout->should_update = true; bool skipLayout = !node->is_dirty(node->context) && eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && eq(layout->last_parent_max_width, parentMaxWidth); eq(layout->last_direction, direction); if (skipLayout) { layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT]; layout->position[CSS_TOP] = layout->last_position[CSS_TOP]; layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT]; } else { layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; layout->last_parent_max_width = parentMaxWidth; layout->last_direction = direction; layoutNodeImpl(node, parentMaxWidth, parentDirection); layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; layout->last_position[CSS_TOP] = layout->position[CSS_TOP]; layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; } } ================================================ FILE: Pod/Classes/Extend/FlexboxKit/Layout.h ================================================ /** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #ifndef __LAYOUT_H #define __LAYOUT_H #include #ifndef __cplusplus #include #endif // Not defined in MSVC++ #ifndef NAN static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; #define NAN (*(const float *)__nan) #endif #define CSS_UNDEFINED NAN typedef enum { CSS_DIRECTION_INHERIT = 0, CSS_DIRECTION_LTR, CSS_DIRECTION_RTL } css_direction_t; typedef enum { CSS_FLEX_DIRECTION_COLUMN = 0, CSS_FLEX_DIRECTION_COLUMN_REVERSE, CSS_FLEX_DIRECTION_ROW, CSS_FLEX_DIRECTION_ROW_REVERSE } css_flex_direction_t; typedef enum { CSS_JUSTIFY_FLEX_START = 0, CSS_JUSTIFY_CENTER, CSS_JUSTIFY_FLEX_END, CSS_JUSTIFY_SPACE_BETWEEN, CSS_JUSTIFY_SPACE_AROUND } css_justify_t; // Note: auto is only a valid value for alignSelf. It is NOT a valid value for // alignItems. typedef enum { CSS_ALIGN_AUTO = 0, CSS_ALIGN_FLEX_START, CSS_ALIGN_CENTER, CSS_ALIGN_FLEX_END, CSS_ALIGN_STRETCH } css_align_t; typedef enum { CSS_POSITION_RELATIVE = 0, CSS_POSITION_ABSOLUTE } css_position_type_t; typedef enum { CSS_NOWRAP = 0, CSS_WRAP } css_wrap_type_t; // Note: left and top are shared between position[2] and position[4], so // they have to be before right and bottom. typedef enum { CSS_LEFT = 0, CSS_TOP, CSS_RIGHT, CSS_BOTTOM, CSS_START, CSS_END, CSS_POSITION_COUNT } css_position_t; typedef enum { CSS_WIDTH = 0, CSS_HEIGHT } css_dimension_t; typedef struct { float position[4]; float dimensions[2]; css_direction_t direction; // Instead of recomputing the entire layout every single time, we // cache some information to break early when nothing changed bool should_update; float last_requested_dimensions[2]; float last_parent_max_width; float last_dimensions[2]; float last_position[2]; css_direction_t last_direction; } css_layout_t; typedef struct { float dimensions[2]; } css_dim_t; typedef struct { css_direction_t direction; css_flex_direction_t flex_direction; css_justify_t justify_content; css_align_t align_content; css_align_t align_items; css_align_t align_self; css_position_type_t position_type; css_wrap_type_t flex_wrap; float flex; float margin[6]; float position[4]; /** * You should skip all the rules that contain negative values for the * following attributes. For example: * {padding: 10, paddingLeft: -5} * should output: * {left: 10 ...} * the following two are incorrect: * {left: -5 ...} * {left: 0 ...} */ float padding[6]; float border[6]; float dimensions[2]; float minDimensions[2]; float maxDimensions[2]; } css_style_t; typedef struct css_node { css_style_t style; css_layout_t layout; int children_count; int line_index; css_dim_t (*measure)(void *context, float width); void (*print)(void *context); struct css_node* (*get_child)(void *context, int i); bool (*is_dirty)(void *context); void *context; } css_node_t; // Lifecycle of nodes and children css_node_t *new_css_node(void); void init_css_node(css_node_t *node); void free_css_node(css_node_t *node); // Print utilities typedef enum { CSS_PRINT_LAYOUT = 1, CSS_PRINT_STYLE = 2, CSS_PRINT_CHILDREN = 4, } css_print_options_t; void print_css_node(css_node_t *node, css_print_options_t options); // Function that computes the layout! void layoutNode(css_node_t *node, float maxWidth, css_direction_t parentDirection); bool isUndefined(float value); #endif ================================================ FILE: Pod/Classes/Extend/FlexboxKit/UIView+FLEXBOX.h ================================================ // // UIView+FLEXBOX.h // FlexboxKit // // Created by Alex Usbergo on 09/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // @import UIKit; #import "FLEXBOXNode.h" @interface UIView (FLEXBOX) // Properties /// YES if this view contains subviews that you wish to layout using the flexbox engine /// @note You don't need to set this if your view is a FLEXBOXContainerView @property (nonatomic, assign) BOOL flexContainer; /// Set this if you wish to have a fixed size for this element @property (nonatomic, assign) CGSize flexFixedSize; /// The minumum size for this element @property (nonatomic, assign) CGSize flexMinimumSize; /// The maximum size for this element @property (nonatomic, assign) CGSize flexMaximumSize; /// It establishes the main-axis, thus defining the direction flex items are placed in the flex container. /// - row: same as text direction (@see FLEXBOXFlexDirectionColumn) /// - column (default): same as row but top to bottom (@see FLEXBOXFlexDirectionRow) /// - row-reverse: (@see FLEXBOXFlexDirectionRowReverse) /// - column-reverse: (@see FLEXBOXFlexDirectionColumnReverse) @property (nonatomic, assign) FLEXBOXFlexDirection flexDirection; /// The margins for this flex item (default is 0) @property (nonatomic, assign) UIEdgeInsets flexMargin; /// The padding for this flex item (default is 0) @property (nonatomic, assign) UIEdgeInsets flexPadding; /// Make the flexible items wrap if necesarry: /// - wrap YES /// - nowrap (default) NO @property (nonatomic, assign) BOOL flexWrap; /// It defines the alignment along the main axis. It helps distribute extra free /// space leftover when either all the flex items on a line are inflexible, or are /// flexible but have reached their maximum size. It also exerts some control over /// the alignment of items when they overflow the line. /// - flex-start (default): items are packed toward the start line (@see FLEXBOXJustificationFlexStart) /// - flex-end: items are packed toward to end line (@see FLEXBOXJustificationFlexEnd) /// - center: items are centered along the line (@see FLEXBOXJustificationCenter) /// - space-between: items are evenly distributed in the line; first item is on the start line, last item on the end line (@see FLEXBOXJustificationSpaceBetween) /// - space-around: items are evenly distributed in the line with equal space around them (@see FLEXBOXJustificationSpaceAround) @property (nonatomic, assign) FLEXBOXJustification flexJustifyContent; /// Center the alignments for one of the items inside a flexible element /// - auto (default): The element inherits its parent container's align-items property, or "stretch" if it has no parent container (@see FLEXBOXAlignmentAuto) /// - stretch: The element is positioned to fit the conatiner (@see FLEXBOXAlignmentStretch) /// - center: The element is positioned at the center of the container (@see FLEXBOXAlignmentCenter) /// - flex-start: The element is are positioned at the beginning of the container (@see FLEXBOXAlignmentFlexStart) /// - flex-end: The element is positioned at the end of the container (@see FLEXBOXAlignmentFlexEnd) @property (nonatomic, assign) FLEXBOXAlignment flexAlignSelf; /// Center the alignments for all the items of the flexible element: /// - stretch (default): The element is positioned to fit the conatiner (@see FLEXBOXAlignmentStretch) /// - center: The element is positioned at the center of the container (@see FLEXBOXAlignmentCenter) /// - flex-start: The element is are positioned at the beginning of the container (@see FLEXBOXAlignmentFlexStart) /// - flex-end: The element is positioned at the end of the container (@see FLEXBOXAlignmentFlexEnd) @property (nonatomic, assign) FLEXBOXAlignment flexAlignItems; /// The flex property specifies the initial length of a flexible item. /// A value between 0 and 1 (a ratio e.g. 1/2, 2/3) @property (nonatomic, assign) CGFloat flex; /// The node content directon (default is inherit) @property (nonatomic, assign) FLEXBOXContentDirection flexContentDirection; // Methods /// Entry point for defining the size for this flex item /// @note By default it calls -[UIView sizeThatFits:] - (CGSize)flexComputeSize:(CGSize)bounds; /// Call this method in -[UIView layoutSubviews] if you want the flexbox /// engine to compute the layout - (void)flexLayoutSubviews; /// Define this block if you want to specify some custom logic instead of /// calling -[UIView sizeThatFits:] in -[UIView flexComputeSize:] @property (nonatomic, copy) CGSize (^flexSizeThatFitsBlock)(CGSize size); @end ================================================ FILE: Pod/Classes/Extend/FlexboxKit/UIView+FLEXBOX.m ================================================ // // UIView+FLEXBOX.m // FlexboxKit // // Created by Alex Usbergo on 09/05/15. // Copyright (c) 2015 Alex Usbergo. All rights reserved. // #import "UIView+FLEXBOX.h" #import "FLEXBOXContainerView.h" #import const void *FLEXBOXContainerKey; const void *FLEXBOXNodeKey; const void *FLEXBOXOffsetNodeKey; const void *FLEXBOXOffsetKey; const void *FLEXBOXSizeKey; const void *FLEXBOXSizeThatFitsBlock; @interface UIView (_FLEXBOX) /// The associated flexbox node @property (nonatomic, strong) FLEXBOXNode *flexNode; @property (nonatomic, readonly, getter=isFlexOffsetNodeDefined) BOOL flexNodeDefined; @end @implementation UIView (FLEXBOX) - (FLEXBOXNode*)flexNode { FLEXBOXNode *node = objc_getAssociatedObject(self, &FLEXBOXNodeKey); if (node == nil) { node = [[FLEXBOXNode alloc] init]; self.flexNode = node; __weak __typeof(self) weakSelf = self; self.flexNode.childrenAtIndexBlock = ^FLEXBOXNode*(NSUInteger i) { return [weakSelf.subviews[i] flexNode]; }; self.flexNode.childrenCountBlock = ^NSUInteger(void) { return weakSelf.subviews.count; }; self.flexNode.measureBlock = ^CGSize(CGFloat width) { return [weakSelf flexComputeSize:(CGSize){width, NAN}]; }; } return node; } - (void)setFlexNode:(FLEXBOXNode*)flexNode { objc_setAssociatedObject(self, &FLEXBOXNodeKey, flexNode, OBJC_ASSOCIATION_RETAIN); } - (BOOL)flexContainer { return [objc_getAssociatedObject(self, &FLEXBOXContainerKey) boolValue]; } - (void)setFlexContainer:(BOOL)flexContainer { objc_setAssociatedObject(self, &FLEXBOXContainerKey, @(flexContainer), OBJC_ASSOCIATION_RETAIN); self.flex = flexContainer ? 1 : 0; } - (CGSize)flexFixedSize { NSValue *value = objc_getAssociatedObject(self, &FLEXBOXSizeKey); if (value != nil) { return [value CGSizeValue]; } else { return CGSizeZero; } } - (void)setFlexFixedSize:(CGSize)flexFixedSize { return objc_setAssociatedObject(self, &FLEXBOXSizeKey, [NSValue valueWithCGSize:flexFixedSize], OBJC_ASSOCIATION_RETAIN); } - (void)setFlexSizeThatFitsBlock:(CGSize (^)(CGSize))sizeThatFitsBlock { objc_setAssociatedObject(self, &FLEXBOXSizeThatFitsBlock, [sizeThatFitsBlock copy], OBJC_ASSOCIATION_COPY); } - (CGSize (^)(CGSize))flexSizeThatFitsBlock { return objc_getAssociatedObject(self, &FLEXBOXSizeThatFitsBlock); } - (CGSize)flexComputeSize:(CGSize)bounds { if (!CGSizeEqualToSize(self.flexFixedSize, CGSizeZero)) return self.flexFixedSize; bounds.height = isnan(bounds.height) ? FLT_MAX : bounds.height; bounds.width = isnan(bounds.width) ? FLT_MAX : bounds.width; CGSize size = CGSizeZero; if (self.flexSizeThatFitsBlock != nil) { size = self.flexSizeThatFitsBlock(bounds); } else { size = [self sizeThatFits:bounds]; } CGSize max = self.flexMaximumSize; if (!CGSizeEqualToSize(max, CGSizeZero) || !CGSizeEqualToSize(max, (CGSize){FLT_MAX, FLT_MAX})) { size.height = !isnan(max.height) && fabs(max.height) > FLT_EPSILON && size.height > max.height ? max.height : size.height; size.width = !isnan(max.width) && fabs(max.width) > FLT_EPSILON && size.width > max.width ? max.width : size.width; } CGSize min = self.flexMinimumSize; if (!CGSizeEqualToSize(min, CGSizeZero) || !CGSizeEqualToSize(max, (CGSize){FLT_MIN, FLT_MIN})) { size.height = !isnan(min.height) && fabs(min.height) > FLT_EPSILON && size.height < min.height ? min.height : size.height; size.width = !isnan(min.width) && fabs(min.width) > FLT_EPSILON && size.width < min.width ? min.width : size.width; } return size; } - (void)flexLayoutSubviews { self.flexNode.dimensions = self.bounds.size; [self _flexLayoutSubviewsFromView:self]; self.frame = (CGRect){self.frame.origin, self.flexNode.frame.size}; } - (void)_flexLayoutSubviewsFromView:(UIView*)view { [view.flexNode layoutConstrainedToMaximumWidth:CGRectGetWidth(view.bounds)]; for (NSUInteger i = 0; i < view.subviews.count; i++) { UIView *subview = view.subviews[i]; FLEXBOXNode *subnode = subview.flexNode; subview.frame = CGRectIntegral(subnode.frame); } for (NSUInteger i = 0; i < view.subviews.count; i++) { UIView *subview = view.subviews[i]; if (subview.flexContainer) [self _flexLayoutSubviewsFromView:subview]; } } #pragma mark - Properties - (FLEXBOXFlexDirection)flexDirection { return self.flexNode.flexDirection; } - (void)setFlexDirection:(FLEXBOXFlexDirection)flexDirection { self.flexNode.flexDirection = flexDirection; } - (UIEdgeInsets)flexMargin { return self.flexNode.margin; } - (void)setFlexMargin:(UIEdgeInsets)flexMargin { self.flexNode.margin = flexMargin; } - (UIEdgeInsets)flexPadding { return self.flexNode.padding; } - (void)setFlexPadding:(UIEdgeInsets)flexPadding { self.flexNode.padding = flexPadding; } - (BOOL)flexWrap { return self.flexNode.flexWrap; } - (void)setFlexWrap:(BOOL)flexWrap { self.flexNode.flexWrap = flexWrap; } - (FLEXBOXJustification)flexJustifyContent { return self.flexNode.justifyContent; } - (void)setFlexJustifyContent:(FLEXBOXJustification)flexJustifyContent { self.flexNode.justifyContent = flexJustifyContent; } - (FLEXBOXAlignment)flexAlignSelf { return self.flexNode.alignSelf; } - (void)setFlexAlignSelf:(FLEXBOXAlignment)flexAlignSelf { self.flexNode.alignSelf = flexAlignSelf; } - (FLEXBOXAlignment)flexAlignItems { return self.flexNode.alignItems; } - (void)setFlexAlignItems:(FLEXBOXAlignment)flexAlignItems { self.flexNode.alignItems = flexAlignItems; } - (CGFloat)flex { return self.flexNode.flex; } - (void)setFlex:(CGFloat)flex { self.flexNode.flex = flex; } - (CGSize)flexMinimumSize { return self.flexNode.minDimensions; } - (void)setFlexMinimumSize:(CGSize)flexMinimumSize { self.flexNode.minDimensions = flexMinimumSize; } - (CGSize)flexMaximumSize { return self.flexNode.maxDimensions; } - (void)setFlexMaximumSize:(CGSize)flexMaximumSize { self.flexNode.maxDimensions = flexMaximumSize; } - (FLEXBOXContentDirection)flexContentDirection { return self.flexNode.contentDirection; } - (void)setFlexContentDirection:(FLEXBOXContentDirection)flexContentDirection { self.flexNode.contentDirection = flexContentDirection; } @end ================================================ FILE: Pod/Classes/Extend/Pinyin/pinyin.h ================================================ /* * pinyin.h * Chinese Pinyin First Letter * * Created by George on 4/21/10. * Copyright 2010 RED/SAFI. All rights reserved. * */ #import @interface HTFirstLetter : NSObject + (char)pinyinFirstLetter:(unsigned short )hanzi; //获取汉字首字母,如果参数既不是汉字也不是英文字母,则返回 @“#” + (NSString *)firstLetter:(NSString *)chineseString; //返回参数中所有汉字的首字母,遇到其他字符,则用 # 替换 + (NSString *)firstLetters:(NSString *)chineseString; @end ================================================ FILE: Pod/Classes/Extend/Pinyin/pinyin.m ================================================ /* * pinyin.c * Chinese Pinyin First Letter * * Created by George on 4/21/10. * Copyright 2010 RED/SAFI. All rights reserved. * */ #define HANZI_START 19968 #define HANZI_COUNT 20902 #import "pinyin.h" static char firstLetterArray[HANZI_COUNT] = "ydkqsxnwzssxjbymgcczqpssqbycdscdqldylybssjgyqzjjfgcclzznwdwzjljpfyynnjjtmynzwzhflzppqhgccyynmjqyxxgd" "nnsnsjnjnsnnmlnrxyfsngnnnnqzggllyjlnyzssecykyyhqwjssggyxyqyjtwktjhychmnxjtlhjyqbyxdldwrrjnwysrldzjpc" "bzjjbrcfslnczstzfxxchtrqggddlyccssymmrjcyqzpwwjjyfcrwfdfzqpyddwyxkyjawjffxjbcftzyhhycyswccyxsclcxxwz" "cxnbgnnxbxlzsqsbsjpysazdhmdzbqbscwdzzyytzhbtsyyfzgntnxjywqnknphhlxgybfmjnbjhhgqtjcysxstkzglyckglysmz" "xyalmeldccxgzyrjxjzlnjzcqkcnnjwhjczccqljststbnhbtyxceqxkkwjyflzqlyhjxspsfxlmpbysxxxytccnylllsjxfhjxp" "jbtffyabyxbcczbzyclwlczggbtssmdtjcxpthyqtgjjxcjfzkjzjqnlzwlslhdzbwjncjzyzsqnycqynzcjjwybrtwpyftwexcs" "kdzctbyhyzqyyjxzcfbzzmjyxxsdczottbzljwfckscsxfyrlrygmbdthjxsqjccsbxyytswfbjdztnbcnzlcyzzpsacyzzsqqcs" "hzqydxlbpjllmqxqydzxsqjtzpxlcglqdcwzfhctdjjsfxjejjtlbgxsxjmyjjqpfzasyjnsydjxkjcdjsznbartcclnjqmwnqnc" "lllkbdbzzsyhqcltwlccrshllzntylnewyzyxczxxgdkdmtcedejtsyyssdqdfmxdbjlkrwnqlybglxnlgtgxbqjdznyjsjyjcjm" "rnymgrcjczgjmzmgxmmryxkjnymsgmzzymknfxmbdtgfbhcjhkylpfmdxlxjjsmsqgzsjlqdldgjycalcmzcsdjllnxdjffffjcn" "fnnffpfkhkgdpqxktacjdhhzdddrrcfqyjkqccwjdxhwjlyllzgcfcqjsmlzpbjjblsbcjggdckkdezsqcckjgcgkdjtjllzycxk" "lqccgjcltfpcqczgwbjdqyzjjbyjhsjddwgfsjgzkcjctllfspkjgqjhzzljplgjgjjthjjyjzccmlzlyqbgjwmljkxzdznjqsyz" "mljlljkywxmkjlhskjhbmclyymkxjqlbmllkmdxxkwyxwslmlpsjqqjqxyqfjtjdxmxxllcrqbsyjbgwynnggbcnxpjtgpapfgdj" "qbhbncfjyzjkjkhxqfgqckfhygkhdkllsdjqxpqyaybnqsxqnszswhbsxwhxwbzzxdmndjbsbkbbzklylxgwxjjwaqzmywsjqlsj" "xxjqwjeqxnchetlzalyyyszzpnkyzcptlshtzcfycyxyljsdcjqagyslcllyyysslqqqnldxzsccscadycjysfsgbfrsszqsbxjp" "sjysdrckgjlgtkzjzbdktcsyqpyhstcldjnhmymcgxyzhjdctmhltxzhylamoxyjcltyfbqqjpfbdfehthsqhzywwcncxcdwhowg" "yjlegmdqcwgfjhcsntmydolbygnqwesqpwnmlrydzszzlyqpzgcwxhnxpyxshmdqjgztdppbfbhzhhjyfdzwkgkzbldnzsxhqeeg" "zxylzmmzyjzgszxkhkhtxexxgylyapsthxdwhzydpxagkydxbhnhnkdnjnmyhylpmgecslnzhkxxlbzzlbmlsfbhhgsgyyggbhsc" "yajtxglxtzmcwzydqdqmngdnllszhngjzwfyhqswscelqajynytlsxthaznkzzsdhlaxxtwwcjhqqtddwzbcchyqzflxpslzqgpz" "sznglydqtbdlxntctajdkywnsyzljhhdzckryyzywmhychhhxhjkzwsxhdnxlyscqydpslyzwmypnkxyjlkchtyhaxqsyshxasmc" "hkdscrsgjpwqsgzjlwwschsjhsqnhnsngndantbaalczmsstdqjcjktscjnxplggxhhgoxzcxpdmmhldgtybynjmxhmrzplxjzck" "zxshflqxxcdhxwzpckczcdytcjyxqhlxdhypjqxnlsyydzozjnhhqezysjyayxkypdgxddnsppyzndhthrhxydpcjjhtcnnctlhb" "ynyhmhzllnnxmylllmdcppxhmxdkycyrdltxjchhznxclcclylnzsxnjzzlnnnnwhyqsnjhxynttdkyjpychhyegkcwtwlgjrlgg" "tgtygyhpyhylqyqgcwyqkpyyettttlhyylltyttsylnyzwgywgpydqqzzdqnnkcqnmjjzzbxtqfjkdffbtkhzkbxdjjkdjjtlbwf" "zpptkqtztgpdwntpjyfalqmkgxbcclzfhzcllllanpnxtjklcclgyhdzfgyddgcyyfgydxkssendhykdndknnaxxhbpbyyhxccga" "pfqyjjdmlxcsjzllpcnbsxgjyndybwjspcwjlzkzddtacsbkzdyzypjzqsjnkktknjdjgyepgtlnyqnacdntcyhblgdzhbbydmjr" "egkzyheyybjmcdtafzjzhgcjnlghldwxjjkytcyksssmtwcttqzlpbszdtwcxgzagyktywxlnlcpbclloqmmzsslcmbjcsdzkydc" "zjgqjdsmcytzqqlnzqzxssbpkdfqmddzzsddtdmfhtdycnaqjqkypbdjyyxtljhdrqxlmhkydhrnlklytwhllrllrcxylbnsrnzz" "symqzzhhkyhxksmzsyzgcxfbnbsqlfzxxnnxkxwymsddyqnggqmmyhcdzttfgyyhgsbttybykjdnkyjbelhdypjqnfxfdnkzhqks" "byjtzbxhfdsbdaswpawajldyjsfhblcnndnqjtjnchxfjsrfwhzfmdrfjyxwzpdjkzyjympcyznynxfbytfyfwygdbnzzzdnytxz" "emmqbsqehxfznbmflzzsrsyqjgsxwzjsprytjsjgskjjgljjynzjjxhgjkymlpyyycxycgqzswhwlyrjlpxslcxmnsmwklcdnkny" "npsjszhdzeptxmwywxyysywlxjqcqxzdclaeelmcpjpclwbxsqhfwrtfnjtnqjhjqdxhwlbyccfjlylkyynldxnhycstyywncjtx" "ywtrmdrqnwqcmfjdxzmhmayxnwmyzqtxtlmrspwwjhanbxtgzypxyyrrclmpamgkqjszycymyjsnxtplnbappypylxmyzkynldgy" "jzcchnlmzhhanqnbgwqtzmxxmllhgdzxnhxhrxycjmffxywcfsbssqlhnndycannmtcjcypnxnytycnnymnmsxndlylysljnlxys" "sqmllyzlzjjjkyzzcsfbzxxmstbjgnxnchlsnmcjscyznfzlxbrnnnylmnrtgzqysatswryhyjzmgdhzgzdwybsscskxsyhytsxg" "cqgxzzbhyxjscrhmkkbsczjyjymkqhzjfnbhmqhysnjnzybknqmcjgqhwlsnzswxkhljhyybqcbfcdsxdldspfzfskjjzwzxsddx" "jseeegjscssygclxxnwwyllymwwwgydkzjggggggsycknjwnjpcxbjjtqtjwdsspjxcxnzxnmelptfsxtllxcljxjjljsxctnswx" "lennlyqrwhsycsqnybyaywjejqfwqcqqcjqgxaldbzzyjgkgxbltqyfxjltpydkyqhpmatlcndnkxmtxynhklefxdllegqtymsaw" "hzmljtkynxlyjzljeeyybqqffnlyxhdsctgjhxywlkllxqkcctnhjlqmkkzgcyygllljdcgydhzwypysjbzjdzgyzzhywyfqdtyz" "szyezklymgjjhtsmqwyzljyywzcsrkqyqltdxwcdrjalwsqzwbdcqyncjnnszjlncdcdtlzzzacqqzzddxyblxcbqjylzllljddz" "jgyqyjzyxnyyyexjxksdaznyrdlzyyynjlslldyxjcykywnqcclddnyyynycgczhjxcclgzqjgnwnncqqjysbzzxyjxjnxjfzbsb" "dsfnsfpzxhdwztdmpptflzzbzdmyypqjrsdzsqzsqxbdgcpzswdwcsqzgmdhzxmwwfybpngphdmjthzsmmbgzmbzjcfzhfcbbnmq" "dfmbcmcjxlgpnjbbxgyhyyjgptzgzmqbqdcgybjxlwnkydpdymgcftpfxyztzxdzxtgkptybbclbjaskytssqyymscxfjhhlslls" "jpqjjqaklyldlycctsxmcwfgngbqxllllnyxtyltyxytdpjhnhgnkbyqnfjyyzbyyessessgdyhfhwtcqbsdzjtfdmxhcnjzymqw" "srxjdzjqbdqbbsdjgnfbknbxdkqhmkwjjjgdllthzhhyyyyhhsxztyyyccbdbpypzyccztjpzywcbdlfwzcwjdxxhyhlhwczxjtc" "nlcdpxnqczczlyxjjcjbhfxwpywxzpcdzzbdccjwjhmlxbqxxbylrddgjrrctttgqdczwmxfytmmzcwjwxyywzzkybzcccttqnhx" "nwxxkhkfhtswoccjybcmpzzykbnnzpbthhjdlszddytyfjpxyngfxbyqxzbhxcpxxtnzdnnycnxsxlhkmzxlthdhkghxxsshqyhh" "cjyxglhzxcxnhekdtgqxqypkdhentykcnymyyjmkqyyyjxzlthhqtbyqhxbmyhsqckwwyllhcyylnneqxqwmcfbdccmljggxdqkt" "lxkknqcdgcjwyjjlyhhqyttnwchhxcxwherzjydjccdbqcdgdnyxzdhcqrxcbhztqcbxwgqwyybxhmbymykdyecmqkyaqyngyzsl" "fnkkqgyssqyshngjctxkzycssbkyxhyylstycxqthysmnscpmmgcccccmnztasmgqzjhklosjylswtmqzyqkdzljqqyplzycztcq" "qpbbcjzclpkhqcyyxxdtdddsjcxffllchqxmjlwcjcxtspycxndtjshjwhdqqqckxyamylsjhmlalygxcyydmamdqmlmcznnyybz" "xkyflmcncmlhxrcjjhsylnmtjggzgywjxsrxcwjgjqhqzdqjdcjjskjkgdzcgjjyjylxzxxcdqhhheslmhlfsbdjsyyshfyssczq" "lpbdrfnztzdkykhsccgkwtqzckmsynbcrxqbjyfaxpzzedzcjykbcjwhyjbqzzywnyszptdkzpfpbaztklqnhbbzptpptyzzybhn" "ydcpzmmcycqmcjfzzdcmnlfpbplngqjtbttajzpzbbdnjkljqylnbzqhksjznggqstzkcxchpzsnbcgzkddzqanzgjkdrtlzldwj" "njzlywtxndjzjhxnatncbgtzcsskmljpjytsnwxcfjwjjtkhtzplbhsnjssyjbhbjyzlstlsbjhdnwqpslmmfbjdwajyzccjtbnn" "nzwxxcdslqgdsdpdzgjtqqpsqlyyjzlgyhsdlctcbjtktyczjtqkbsjlgnnzdncsgpynjzjjyyknhrpwszxmtncszzyshbyhyzax" "ywkcjtllckjjtjhgcssxyqyczbynnlwqcglzgjgqyqcczssbcrbcskydznxjsqgxssjmecnstjtpbdlthzwxqwqczexnqczgwesg" "ssbybstscslccgbfsdqnzlccglllzghzcthcnmjgyzazcmsksstzmmzckbjygqljyjppldxrkzyxccsnhshhdznlzhzjjcddcbcj" "xlbfqbczztpqdnnxljcthqzjgylklszzpcjdscqjhjqkdxgpbajynnsmjtzdxlcjyryynhjbngzjkmjxltbsllrzpylssznxjhll" "hyllqqzqlsymrcncxsljmlzltzldwdjjllnzggqxppskyggggbfzbdkmwggcxmcgdxjmcjsdycabxjdlnbcddygskydqdxdjjyxh" "saqazdzfslqxxjnqzylblxxwxqqzbjzlfbblylwdsljhxjyzjwtdjcyfqzqzzdzsxzzqlzcdzfxhwspynpqzmlpplffxjjnzzyls" "jnyqzfpfzgsywjjjhrdjzzxtxxglghtdxcskyswmmtcwybazbjkshfhgcxmhfqhyxxyzftsjyzbxyxpzlchmzmbxhzzssyfdmncw" "dabazlxktcshhxkxjjzjsthygxsxyyhhhjwxkzxssbzzwhhhcwtzzzpjxsyxqqjgzyzawllcwxznxgyxyhfmkhydwsqmnjnaycys" "pmjkgwcqhylajgmzxhmmcnzhbhxclxdjpltxyjkdyylttxfqzhyxxsjbjnayrsmxyplckdnyhlxrlnllstycyyqygzhhsccsmcct" "zcxhyqfpyyrpbflfqnntszlljmhwtcjqyzwtlnmlmdwmbzzsnzrbpdddlqjjbxtcsnzqqygwcsxfwzlxccrszdzmcyggdyqsgtnn" "nlsmymmsyhfbjdgyxccpshxczcsbsjyygjmpbwaffyfnxhydxzylremzgzzyndsznlljcsqfnxxkptxzgxjjgbmyyssnbtylbnlh" "bfzdcyfbmgqrrmzszxysjtznnydzzcdgnjafjbdknzblczszpsgcycjszlmnrznbzzldlnllysxsqzqlcxzlsgkbrxbrbzcycxzj" "zeeyfgklzlnyhgzcgzlfjhgtgwkraajyzkzqtsshjjxdzyznynnzyrzdqqhgjzxsszbtkjbbfrtjxllfqwjgclqtymblpzdxtzag" "bdhzzrbgjhwnjtjxlkscfsmwlldcysjtxkzscfwjlbnntzlljzllqblcqmqqcgcdfpbphzczjlpyyghdtgwdxfczqyyyqysrclqz" "fklzzzgffcqnwglhjycjjczlqzzyjbjzzbpdcsnnjgxdqnknlznnnnpsntsdyfwwdjzjysxyyczcyhzwbbyhxrylybhkjksfxtjj" "mmchhlltnyymsxxyzpdjjycsycwmdjjkqyrhllngpngtlyycljnnnxjyzfnmlrgjjtyzbsyzmsjyjhgfzqmsyxrszcytlrtqzsst" "kxgqkgsptgxdnjsgcqcqhmxggztqydjjznlbznxqlhyqgggthqscbyhjhhkyygkggcmjdzllcclxqsftgjslllmlcskctbljszsz" "mmnytpzsxqhjcnnqnyexzqzcpshkzzyzxxdfgmwqrllqxrfztlystctmjcsjjthjnxtnrztzfqrhcgllgcnnnnjdnlnnytsjtlny" "xsszxcgjzyqpylfhdjsbbdczgjjjqzjqdybssllcmyttmqnbhjqmnygjyeqyqmzgcjkpdcnmyzgqllslnclmholzgdylfzslncnz" "lylzcjeshnyllnxnjxlyjyyyxnbcljsswcqqnnyllzldjnllzllbnylnqchxyyqoxccqkyjxxxyklksxeyqhcqkkkkcsnyxxyqxy" "gwtjohthxpxxhsnlcykychzzcbwqbbwjqcscszsslcylgddsjzmmymcytsdsxxscjpqqsqylyfzychdjynywcbtjsydchcyddjlb" "djjsodzyqyskkyxdhhgqjyohdyxwgmmmazdybbbppbcmnnpnjzsmtxerxjmhqdntpjdcbsnmssythjtslmltrcplzszmlqdsdmjm" "qpnqdxcfrnnfsdqqyxhyaykqyddlqyyysszbydslntfgtzqbzmchdhczcwfdxtmqqsphqwwxsrgjcwnntzcqmgwqjrjhtqjbbgwz" "fxjhnqfxxqywyyhyscdydhhqmrmtmwctbszppzzglmzfollcfwhmmsjzttdhlmyffytzzgzyskjjxqyjzqbhmbzclyghgfmshpcf" "zsnclpbqsnjyzslxxfpmtyjygbxlldlxpzjyzjyhhzcywhjylsjexfszzywxkzjlnadymlymqjpwxxhxsktqjezrpxxzghmhwqpw" "qlyjjqjjzszcnhjlchhnxjlqwzjhbmzyxbdhhypylhlhlgfwlcfyytlhjjcwmscpxstkpnhjxsntyxxtestjctlsslstdlllwwyh" "dnrjzsfgxssyczykwhtdhwjglhtzdqdjzxxqgghltzphcsqfclnjtclzpfstpdynylgmjllycqhynspchylhqyqtmzymbywrfqyk" "jsyslzdnjmpxyyssrhzjnyqtqdfzbwwdwwrxcwggyhxmkmyyyhmxmzhnksepmlqqmtcwctmxmxjpjjhfxyyzsjzhtybmstsyjznq" "jnytlhynbyqclcycnzwsmylknjxlggnnpjgtysylymzskttwlgsmzsylmpwlcwxwqcssyzsyxyrhssntsrwpccpwcmhdhhxzdzyf" "jhgzttsbjhgyglzysmyclllxbtyxhbbzjkssdmalhhycfygmqypjyjqxjllljgclzgqlycjcctotyxmtmshllwlqfxymzmklpszz" "cxhkjyclctyjcyhxsgyxnnxlzwpyjpxhjwpjpwxqqxlxsdhmrslzzydwdtcxknstzshbsccstplwsscjchjlcgchssphylhfhhxj" "sxallnylmzdhzxylsxlmzykcldyahlcmddyspjtqjzlngjfsjshctsdszlblmssmnyymjqbjhrzwtyydchjljapzwbgqxbkfnbjd" "llllyylsjydwhxpsbcmljpscgbhxlqhyrljxyswxhhzlldfhlnnymjljyflyjycdrjlfsyzfsllcqyqfgqyhnszlylmdtdjcnhbz" "llnwlqxygyyhbmgdhxxnhlzzjzxczzzcyqzfngwpylcpkpykpmclgkdgxzgxwqbdxzzkzfbddlzxjtpjpttbythzzdwslcpnhslt" "jxxqlhyxxxywzyswttzkhlxzxzpyhgzhknfsyhntjrnxfjcpjztwhplshfcrhnslxxjxxyhzqdxqwnnhyhmjdbflkhcxcwhjfyjc" "fpqcxqxzyyyjygrpynscsnnnnchkzdyhflxxhjjbyzwttxnncyjjymswyxqrmhxzwfqsylznggbhyxnnbwttcsybhxxwxyhhxyxn" "knyxmlywrnnqlxbbcljsylfsytjzyhyzawlhorjmnsczjxxxyxchcyqryxqzddsjfslyltsffyxlmtyjmnnyyyxltzcsxqclhzxl" "wyxzhnnlrxkxjcdyhlbrlmbrdlaxksnlljlyxxlynrylcjtgncmtlzllcyzlpzpzyawnjjfybdyyzsepckzzqdqpbpsjpdyttbdb" "bbyndycncpjmtmlrmfmmrwyfbsjgygsmdqqqztxmkqwgxllpjgzbqrdjjjfpkjkcxbljmswldtsjxldlppbxcwkcqqbfqbccajzg" "mykbhyhhzykndqzybpjnspxthlfpnsygyjdbgxnhhjhzjhstrstldxskzysybmxjlxyslbzyslzxjhfybqnbylljqkygzmcyzzym" "ccslnlhzhwfwyxzmwyxtynxjhbyymcysbmhysmydyshnyzchmjjmzcaahcbjbbhblytylsxsnxgjdhkxxtxxnbhnmlngsltxmrhn" "lxqqxmzllyswqgdlbjhdcgjyqyymhwfmjybbbyjyjwjmdpwhxqldyapdfxxbcgjspckrssyzjmslbzzjfljjjlgxzgyxyxlszqkx" "bexyxhgcxbpndyhwectwwcjmbtxchxyqqllxflyxlljlssnwdbzcmyjclwswdczpchqekcqbwlcgydblqppqzqfnqdjhymmcxtxd" "rmzwrhxcjzylqxdyynhyyhrslnrsywwjjymtltllgtqcjzyabtckzcjyccqlysqxalmzynywlwdnzxqdllqshgpjfjljnjabcqzd" "jgthhsstnyjfbswzlxjxrhgldlzrlzqzgsllllzlymxxgdzhgbdphzpbrlwnjqbpfdwonnnhlypcnjccndmbcpbzzncyqxldomzb" "lzwpdwyygdstthcsqsccrsssyslfybnntyjszdfndpdhtqzmbqlxlcmyffgtjjqwftmnpjwdnlbzcmmcngbdzlqlpnfhyymjylsd" "chdcjwjcctljcldtljjcbddpndsszycndbjlggjzxsxnlycybjjxxcbylzcfzppgkcxqdzfztjjfjdjxzbnzyjqctyjwhdyczhym" "djxttmpxsplzcdwslshxypzgtfmlcjtacbbmgdewycyzxdszjyhflystygwhkjyylsjcxgywjcbllcsnddbtzbsclyzczzssqdll" "mjyyhfllqllxfdyhabxggnywyypllsdldllbjcyxjznlhljdxyyqytdlllbngpfdfbbqbzzmdpjhgclgmjjpgaehhbwcqxajhhhz" "chxyphjaxhlphjpgpzjqcqzgjjzzgzdmqyybzzphyhybwhazyjhykfgdpfqsdlzmljxjpgalxzdaglmdgxmmzqwtxdxxpfdmmssy" "mpfmdmmkxksyzyshdzkjsysmmzzzmdydyzzczxbmlstmdyemxckjmztyymzmzzmsshhdccjewxxkljsthwlsqlyjzllsjssdppmh" "nlgjczyhmxxhgncjmdhxtkgrmxfwmckmwkdcksxqmmmszzydkmsclcmpcjmhrpxqpzdsslcxkyxtwlkjyahzjgzjwcjnxyhmmbml" "gjxmhlmlgmxctkzmjlyscjsyszhsyjzjcdajzhbsdqjzgwtkqxfkdmsdjlfmnhkzqkjfeypzyszcdpynffmzqykttdzzefmzlbnp" "plplpbpszalltnlkckqzkgenjlwalkxydpxnhsxqnwqnkxqclhyxxmlnccwlymqyckynnlcjnszkpyzkcqzqljbdmdjhlasqlbyd" "wqlwdgbqcryddztjybkbwszdxdtnpjdtcnqnfxqqmgnseclstbhpwslctxxlpwydzklnqgzcqapllkqcylbqmqczqcnjslqzdjxl" "ddhpzqdljjxzqdjyzhhzlkcjqdwjppypqakjyrmpzbnmcxkllzllfqpylllmbsglzysslrsysqtmxyxzqzbscnysyztffmzzsmzq" "hzssccmlyxwtpzgxzjgzgsjzgkddhtqggzllbjdzlsbzhyxyzhzfywxytymsdnzzyjgtcmtnxqyxjscxhslnndlrytzlryylxqht" "xsrtzcgyxbnqqzfhykmzjbzymkbpnlyzpblmcnqyzzzsjztjctzhhyzzjrdyzhnfxklfzslkgjtctssyllgzrzbbjzzklpkbczys" "nnyxbjfbnjzzxcdwlzyjxzzdjjgggrsnjkmsmzjlsjywqsnyhqjsxpjztnlsnshrnynjtwchglbnrjlzxwjqxqkysjycztlqzybb" "ybyzjqdwgyzcytjcjxckcwdkkzxsnkdnywwyyjqyytlytdjlxwkcjnklccpzcqqdzzqlcsfqchqqgssmjzzllbjjzysjhtsjdysj" "qjpdszcdchjkjzzlpycgmzndjxbsjzzsyzyhgxcpbjydssxdzncglqmbtsfcbfdzdlznfgfjgfsmpnjqlnblgqcyyxbqgdjjqsrf" "kztjdhczklbsdzcfytplljgjhtxzcsszzxstjygkgckgynqxjplzbbbgcgyjzgczqszlbjlsjfzgkqqjcgycjbzqtldxrjnbsxxp" "zshszycfwdsjjhxmfczpfzhqhqmqnknlyhtycgfrzgnqxcgpdlbzcsczqlljblhbdcypscppdymzzxgyhckcpzjgslzlnscnsldl" "xbmsdlddfjmkdqdhslzxlsznpqpgjdlybdskgqlbzlnlkyyhzttmcjnqtzzfszqktlljtyyllnllqyzqlbdzlslyyzxmdfszsnxl" "xznczqnbbwskrfbcylctnblgjpmczzlstlxshtzcyzlzbnfmqnlxflcjlyljqcbclzjgnsstbrmhxzhjzclxfnbgxgtqncztmsfz" "kjmssncljkbhszjntnlzdntlmmjxgzjyjczxyhyhwrwwqnztnfjscpyshzjfyrdjsfscjzbjfzqzchzlxfxsbzqlzsgyftzdcszx" "zjbjpszkjrhxjzcgbjkhcggtxkjqglxbxfgtrtylxqxhdtsjxhjzjjcmzlcqsbtxwqgxtxxhxftsdkfjhzyjfjxnzldlllcqsqqz" "qwqxswqtwgwbzcgcllqzbclmqjtzgzyzxljfrmyzflxnsnxxjkxrmjdzdmmyxbsqbhgzmwfwygmjlzbyytgzyccdjyzxsngnyjyz" "nbgpzjcqsyxsxrtfyzgrhztxszzthcbfclsyxzlzqmzlmplmxzjssfsbysmzqhxxnxrxhqzzzsslyflczjrcrxhhzxqndshxsjjh" "qcjjbcynsysxjbqjpxzqplmlxzkyxlxcnlcycxxzzlxdlllmjyhzxhyjwkjrwyhcpsgnrzlfzwfzznsxgxflzsxzzzbfcsyjdbrj" "krdhhjxjljjtgxjxxstjtjxlyxqfcsgswmsbctlqzzwlzzkxjmltmjyhsddbxgzhdlbmyjfrzfcgclyjbpmlysmsxlszjqqhjzfx" "gfqfqbphngyyqxgztnqwyltlgwgwwhnlfmfgzjmgmgbgtjflyzzgzyzaflsspmlbflcwbjztljjmzlpjjlymqtmyyyfbgygqzgly" "zdxqyxrqqqhsxyyqxygjtyxfsfsllgnqcygycwfhcccfxpylypllzqxxxxxqqhhsshjzcftsczjxspzwhhhhhapylqnlpqafyhxd" "ylnkmzqgggddesrenzltzgchyppcsqjjhclljtolnjpzljlhymhezdydsqycddhgznndzclzywllznteydgnlhslpjjbdgwxpcnn" "tycklkclwkllcasstknzdnnjttlyyzssysszzryljqkcgdhhyrxrzydgrgcwcgzqffbppjfzynakrgywyjpqxxfkjtszzxswzddf" "bbqtbgtzkznpzfpzxzpjszbmqhkyyxyldkljnypkyghgdzjxxeaxpnznctzcmxcxmmjxnkszqnmnlwbwwqjjyhclstmcsxnjcxxt" "pcnfdtnnpglllzcjlspblpgjcdtnjjlyarscffjfqwdpgzdwmrzzcgodaxnssnyzrestyjwjyjdbcfxnmwttbqlwstszgybljpxg" "lbnclgpcbjftmxzljylzxcltpnclcgxtfzjshcrxsfysgdkntlbyjcyjllstgqcbxnhzxbxklylhzlqzlnzcqwgzlgzjncjgcmnz" "zgjdzxtzjxycyycxxjyyxjjxsssjstsstdppghtcsxwzdcsynptfbchfbblzjclzzdbxgcjlhpxnfzflsyltnwbmnjhszbmdnbcy" "sccldnycndqlyjjhmqllcsgljjsyfpyyccyltjantjjpwycmmgqyysxdxqmzhszxbftwwzqswqrfkjlzjqqyfbrxjhhfwjgzyqac" "myfrhcyybynwlpexcczsyyrlttdmqlrkmpbgmyyjprkznbbsqyxbhyzdjdnghpmfsgbwfzmfqmmbzmzdcgjlnnnxyqgmlrygqccy" "xzlwdkcjcggmcjjfyzzjhycfrrcmtznzxhkqgdjxccjeascrjthpljlrzdjrbcqhjdnrhylyqjsymhzydwcdfryhbbydtssccwbx" "glpzmlzjdqsscfjmmxjcxjytycghycjwynsxlfemwjnmkllswtxhyyyncmmcyjdqdjzglljwjnkhpzggflccsczmcbltbhbqjxqd" "jpdjztghglfjawbzyzjltstdhjhctcbchflqmpwdshyytqwcnntjtlnnmnndyyyxsqkxwyyflxxnzwcxypmaelyhgjwzzjbrxxaq" "jfllpfhhhytzzxsgqjmhspgdzqwbwpjhzjdyjcqwxkthxsqlzyymysdzgnqckknjlwpnsyscsyzlnmhqsyljxbcxtlhzqzpcycyk" "pppnsxfyzjjrcemhszmnxlxglrwgcstlrsxbygbzgnxcnlnjlclynymdxwtzpalcxpqjcjwtcyyjlblxbzlqmyljbghdslssdmxm" "bdczsxyhamlczcpjmcnhjyjnsykchskqmczqdllkablwjqsfmocdxjrrlyqchjmybyqlrhetfjzfrfksryxfjdwtsxxywsqjysly" "xwjhsdlxyyxhbhawhwjcxlmyljcsqlkydttxbzslfdxgxsjkhsxxybssxdpwncmrptqzczenygcxqfjxkjbdmljzmqqxnoxslyxx" "lylljdzptymhbfsttqqwlhsgynlzzalzxclhtwrrqhlstmypyxjjxmnsjnnbryxyjllyqyltwylqyfmlkljdnlltfzwkzhljmlhl" "jnljnnlqxylmbhhlnlzxqchxcfxxlhyhjjgbyzzkbxscqdjqdsndzsygzhhmgsxcsymxfepcqwwrbpyyjqryqcyjhqqzyhmwffhg" "zfrjfcdbxntqyzpcyhhjlfrzgpbxzdbbgrqstlgdgylcqmgchhmfywlzyxkjlypjhsywmqqggzmnzjnsqxlqsyjtcbehsxfszfxz" "wfllbcyyjdytdthwzsfjmqqyjlmqsxlldttkghybfpwdyysqqrnqwlgwdebzwcyygcnlkjxtmxmyjsxhybrwfymwfrxyymxysctz" "ztfykmldhqdlgyjnlcryjtlpsxxxywlsbrrjwxhqybhtydnhhxmmywytycnnmnssccdalwztcpqpyjllqzyjswjwzzmmglmxclmx" "nzmxmzsqtzppjqblpgxjzhfljjhycjsrxwcxsncdlxsyjdcqzxslqyclzxlzzxmxqrjmhrhzjbhmfljlmlclqnldxzlllfyprgjy" "nxcqqdcmqjzzxhnpnxzmemmsxykynlxsxtljxyhwdcwdzhqyybgybcyscfgfsjnzdrzzxqxrzrqjjymcanhrjtldbpyzbstjhxxz" "ypbdwfgzzrpymnnkxcqbyxnbnfyckrjjcmjegrzgyclnnzdnkknsjkcljspgyyclqqjybzssqlllkjftbgtylcccdblsppfylgyd" "tzjqjzgkntsfcxbdkdxxhybbfytyhbclnnytgdhryrnjsbtcsnyjqhklllzslydxxwbcjqsbxnpjzjzjdzfbxxbrmladhcsnclbj" "dstblprznswsbxbcllxxlzdnzsjpynyxxyftnnfbhjjjgbygjpmmmmsszljmtlyzjxswxtyledqpjmpgqzjgdjlqjwjqllsdgjgy" "gmscljjxdtygjqjjjcjzcjgdzdshqgzjggcjhqxsnjlzzbxhsgzxcxyljxyxyydfqqjhjfxdhctxjyrxysqtjxyefyyssyxjxncy" "zxfxcsxszxyyschshxzzzgzzzgfjdldylnpzgsjaztyqzpbxcbdztzczyxxyhhscjshcggqhjhgxhsctmzmehyxgebtclzkkwytj" "zrslekestdbcyhqqsayxcjxwwgsphjszsdncsjkqcxswxfctynydpccczjqtcwjqjzzzqzljzhlsbhpydxpsxshhezdxfptjqyzc" "xhyaxncfzyyhxgnqmywntzsjbnhhgymxmxqcnssbcqsjyxxtyyhybcqlmmszmjzzllcogxzaajzyhjmchhcxzsxsdznleyjjzjbh" "zwjzsqtzpsxzzdsqjjjlnyazphhyysrnqzthzhnyjyjhdzxzlswclybzyecwcycrylchzhzydzydyjdfrjjhtrsqtxyxjrjhojyn" "xelxsfsfjzghpzsxzszdzcqzbyyklsgsjhczshdgqgxyzgxchxzjwyqwgyhksseqzzndzfkwyssdclzstsymcdhjxxyweyxczayd" "mpxmdsxybsqmjmzjmtjqlpjyqzcgqhyjhhhqxhlhdldjqcfdwbsxfzzyyschtytyjbhecxhjkgqfxbhyzjfxhwhbdzfyzbchpnpg" "dydmsxhkhhmamlnbyjtmpxejmcthqbzyfcgtyhwphftgzzezsbzegpbmdskftycmhbllhgpzjxzjgzjyxzsbbqsczzlzscstpgxm" "jsfdcczjzdjxsybzlfcjsazfgszlwbczzzbyztzynswyjgxzbdsynxlgzbzfygczxbzhzftpbgzgejbstgkdmfhyzzjhzllzzgjq" "zlsfdjsscbzgpdlfzfzszyzyzsygcxsnxxchczxtzzljfzgqsqqxcjqccccdjcdszzyqjccgxztdlgscxzsyjjqtcclqdqztqchq" "qyzynzzzpbkhdjfcjfztypqyqttynlmbdktjcpqzjdzfpjsbnjlgyjdxjdcqkzgqkxclbzjtcjdqbxdjjjstcxnxbxqmslyjcxnt" "jqwwcjjnjjlllhjcwqtbzqqczczpzzdzyddcyzdzccjgtjfzdprntctjdcxtqzdtjnplzbcllctdsxkjzqdmzlbznbtjdcxfczdb" "czjjltqqpldckztbbzjcqdcjwynllzlzccdwllxwzlxrxntqjczxkjlsgdnqtddglnlajjtnnynkqlldzntdnycygjwyxdxfrsqs" "tcdenqmrrqzhhqhdldazfkapbggpzrebzzykyqspeqjjglkqzzzjlysyhyzwfqznlzzlzhwcgkypqgnpgblplrrjyxcccgyhsfzf" "wbzywtgzxyljczwhncjzplfflgskhyjdeyxhlpllllcygxdrzelrhgklzzyhzlyqszzjzqljzflnbhgwlczcfjwspyxzlzlxgccp" "zbllcxbbbbnbbcbbcrnnzccnrbbnnldcgqyyqxygmqzwnzytyjhyfwtehznjywlccntzyjjcdedpwdztstnjhtymbjnyjzlxtsst" "phndjxxbyxqtzqddtjtdyztgwscszqflshlnzbcjbhdlyzjyckwtydylbnydsdsycctyszyyebgexhqddwnygyclxtdcystqnygz" "ascsszzdzlcclzrqxyywljsbymxshzdembbllyyllytdqyshymrqnkfkbfxnnsbychxbwjyhtqbpbsbwdzylkgzskyghqzjxhxjx" "gnljkzlyycdxlfwfghljgjybxblybxqpqgntzplncybxdjyqydymrbeyjyyhkxxstmxrczzjwxyhybmcflyzhqyzfwxdbxbcwzms" "lpdmyckfmzklzcyqycclhxfzlydqzpzygyjyzmdxtzfnnyttqtzhgsfcdmlccytzxjcytjmkslpzhysnwllytpzctzccktxdhxxt" "qcyfksmqccyyazhtjplylzlyjbjxtfnyljyynrxcylmmnxjsmybcsysslzylljjgyldzdlqhfzzblfndsqkczfyhhgqmjdsxyctt" "xnqnjpyybfcjtyyfbnxejdgyqbjrcnfyyqpghyjsyzngrhtknlnndzntsmgklbygbpyszbydjzsstjztsxzbhbscsbzczptqfzlq" "flypybbjgszmnxdjmtsyskkbjtxhjcegbsmjyjzcstmljyxrczqscxxqpyzhmkyxxxjcljyrmyygadyskqlnadhrskqxzxztcggz" "dlmlwxybwsyctbhjhcfcwzsxwwtgzlxqshnyczjxemplsrcgltnzntlzjcyjgdtclglbllqpjmzpapxyzlaktkdwczzbncctdqqz" "qyjgmcdxltgcszlmlhbglkznnwzndxnhlnmkydlgxdtwcfrjerctzhydxykxhwfzcqshknmqqhzhhymjdjskhxzjzbzzxympajnm" "ctbxlsxlzynwrtsqgscbptbsgzwyhtlkssswhzzlyytnxjgmjrnsnnnnlskztxgxlsammlbwldqhylakqcqctmycfjbslxclzjcl" "xxknbnnzlhjphqplsxsckslnhpsfqcytxjjzljldtzjjzdlydjntptnndskjfsljhylzqqzlbthydgdjfdbyadxdzhzjnthqbykn" "xjjqczmlljzkspldsclbblnnlelxjlbjycxjxgcnlcqplzlznjtsljgyzdzpltqcssfdmnycxgbtjdcznbgbqyqjwgkfhtnbyqzq" "gbkpbbyzmtjdytblsqmbsxtbnpdxklemyycjynzdtldykzzxtdxhqshygmzsjycctayrzlpwltlkxslzcggexclfxlkjrtlqjaqz" "ncmbqdkkcxglczjzxjhptdjjmzqykqsecqzdshhadmlzfmmzbgntjnnlhbyjbrbtmlbyjdzxlcjlpldlpcqdhlhzlycblcxccjad" "qlmzmmsshmybhbnkkbhrsxxjmxmdznnpklbbrhgghfchgmnklltsyyycqlcskymyehywxnxqywbawykqldnntndkhqcgdqktgpkx" "hcpdhtwnmssyhbwcrwxhjmkmzngwtmlkfghkjyldyycxwhyyclqhkqhtdqkhffldxqwytyydesbpkyrzpjfyyzjceqdzzdlattpb" "fjllcxdlmjsdxegwgsjqxcfbssszpdyzcxznyxppzydlyjccpltxlnxyzyrscyyytylwwndsahjsygyhgywwaxtjzdaxysrltdps" "syxfnejdxyzhlxlllzhzsjnyqyqyxyjghzgjcyjchzlycdshhsgczyjscllnxzjjyyxnfsmwfpyllyllabmddhwzxjmcxztzpmlq" "chsfwzynctlndywlslxhymmylmbwwkyxyaddxylldjpybpwnxjmmmllhafdllaflbnhhbqqjqzjcqjjdjtffkmmmpythygdrjrdd" "wrqjxnbysrmzdbyytbjhpymyjtjxaahggdqtmystqxkbtzbkjlxrbyqqhxmjjbdjntgtbxpgbktlgqxjjjcdhxqdwjlwrfmjgwqh" "cnrxswgbtgygbwhswdwrfhwytjjxxxjyzyslphyypyyxhydqpxshxyxgskqhywbdddpplcjlhqeewjgsyykdpplfjthkjltcyjhh" "jttpltzzcdlyhqkcjqysteeyhkyzyxxyysddjkllpymqyhqgxqhzrhbxpllnqydqhxsxxwgdqbshyllpjjjthyjkyphthyyktyez" "yenmdshlzrpqfbnfxzbsftlgxsjbswyysksflxlpplbbblnsfbfyzbsjssylpbbffffsscjdstjsxtryjcyffsyzyzbjtlctsbsd" "hrtjjbytcxyyeylycbnebjdsysyhgsjzbxbytfzwgenhhhthjhhxfwgcstbgxklstyymtmbyxjskzscdyjrcythxzfhmymcxlzns" "djtxtxrycfyjsbsdyerxhljxbbdeynjghxgckgscymblxjmsznskgxfbnbbthfjyafxwxfbxmyfhdttcxzzpxrsywzdlybbktyqw" "qjbzypzjznjpzjlztfysbttslmptzrtdxqsjehbnylndxljsqmlhtxtjecxalzzspktlzkqqyfsyjywpcpqfhjhytqxzkrsgtksq" "czlptxcdyyzsslzslxlzmacpcqbzyxhbsxlzdltztjtylzjyytbzypltxjsjxhlbmytxcqrblzssfjzztnjytxmyjhlhpblcyxqj" "qqkzzscpzkswalqsplczzjsxgwwwygyatjbbctdkhqhkgtgpbkqyslbxbbckbmllndzstbklggqkqlzbkktfxrmdkbftpzfrtppm" "ferqnxgjpzsstlbztpszqzsjdhljqlzbpmsmmsxlqqnhknblrddnhxdkddjcyyljfqgzlgsygmjqjkhbpmxyxlytqwlwjcpbmjxc" "yzydrjbhtdjyeqshtmgsfyplwhlzffnynnhxqhpltbqpfbjwjdbygpnxtbfzjgnnntjshxeawtzylltyqbwjpgxghnnkndjtmszs" "qynzggnwqtfhclssgmnnnnynzqqxncjdqgzdlfnykljcjllzlmzznnnnsshthxjlzjbbhqjwwycrdhlyqqjbeyfsjhthnrnwjhwp" "slmssgzttygrqqwrnlalhmjtqjsmxqbjjzjqzyzkxbjqxbjxshzssfglxmxnxfghkzszggslcnnarjxhnlllmzxelglxydjytlfb" "kbpnlyzfbbhptgjkwetzhkjjxzxxglljlstgshjjyqlqzfkcgnndjsszfdbctwwseqfhqjbsaqtgypjlbxbmmywxgslzhglsgnyf" "ljbyfdjfngsfmbyzhqffwjsyfyjjphzbyyzffwotjnlmftwlbzgyzqxcdjygzyyryzynyzwegazyhjjlzrthlrmgrjxzclnnnljj" "yhtbwjybxxbxjjtjteekhwslnnlbsfazpqqbdlqjjtyyqlyzkdksqjnejzldqcgjqnnjsncmrfqthtejmfctyhypymhydmjncfgy" "yxwshctxrljgjzhzcyyyjltkttntmjlzclzzayyoczlrlbszywjytsjyhbyshfjlykjxxtmzyyltxxypslqyjzyzyypnhmymdyyl" "blhlsyygqllnjjymsoycbzgdlyxylcqyxtszegxhzglhwbljheyxtwqmakbpqcgyshhegqcmwyywljyjhyyzlljjylhzyhmgsljl" "jxcjjyclycjbcpzjzjmmwlcjlnqljjjlxyjmlszljqlycmmgcfmmfpqqmfxlqmcffqmmmmhnznfhhjgtthxkhslnchhyqzxtmmqd" "cydyxyqmyqylddcyaytazdcymdydlzfffmmycqcwzzmabtbyctdmndzggdftypcgqyttssffwbdttqssystwnjhjytsxxylbyyhh" "whxgzxwznnqzjzjjqjccchykxbzszcnjtllcqxynjnckycynccqnxyewyczdcjycchyjlbtzyycqwlpgpyllgktltlgkgqbgychj" "xy"; @interface NSString (enumrateCharater) - (void)enumerateCharaterUsingBlock:(void(^)(unsigned short letter, BOOL *stop))enumertae; @end @implementation NSString (enumrateCharater) - (void)enumerateCharaterUsingBlock:(void(^)(unsigned short letter, BOOL *stop))enumertae { static BOOL stop; for (NSInteger i = 0; i < [self length]; i++) { NSLog(@"letter1:%c", [self characterAtIndex:i]); enumertae([self characterAtIndex:i], &stop); if (stop) break; } } @end @implementation HTFirstLetter + (char)pinyinFirstLetter:(unsigned short )hanzi { int index = hanzi - HANZI_START; if (index >= 0 && index <= HANZI_COUNT) { return firstLetterArray[index]; } else { return '#'; } } + (NSString *)firstLetter:(NSString *)chineseString { NSString *firstLetters = [HTFirstLetter firstLetters:chineseString]; NSArray *firstLetterArray = [firstLetters componentsSeparatedByString:@" "]; if ([firstLetterArray count] > 0) firstLetters = [firstLetterArray objectAtIndex:0]; return firstLetters; } + (NSString *)firstLetters:(NSString *)chineseString { return [HTFirstLetter firstLetterUsingSeperate:@" " chineseString:chineseString]; } + (NSString *)firstLetterUsingSeperate:(NSString *)seperate chineseString:(NSString *)chineseString { __block NSString *firstLetters = @""; [chineseString enumerateCharaterUsingBlock:^(unsigned short letter, BOOL *stop) { int index = letter - HANZI_START; if (index >= 0 && index <= HANZI_COUNT){ if ([firstLetters length]){ firstLetters = [firstLetters stringByAppendingFormat:@" %c", firstLetterArray[index]]; }else { firstLetters = [firstLetters stringByAppendingFormat:@"%c", firstLetterArray[index]]; } }else if ((letter > 'a' && letter < 'z') || (letter > 'A' && letter < 'Z')){ if ([firstLetters length]){ firstLetters = [firstLetters stringByAppendingFormat:@" %c", letter]; }else { firstLetters = [firstLetters stringByAppendingFormat:@"%c", letter]; } }else { //如果是字母或其它符号,都返回 # if ([firstLetters length]){ firstLetters = [firstLetters stringByAppendingFormat:@" %c",true ? '#' : letter]; }else { firstLetters = [firstLetters stringByAppendingFormat:@"%c", true ? '#' : letter]; } } }]; return firstLetters; } @end ================================================ FILE: Pod/Classes/Extend/SwiftRegex/Match.swift ================================================ // // Match.swift // Cupertino // // Created by William Kent on 2/19/15. // Copyright (c) 2015 William Kent. All rights reserved. // import Foundation private func safeSubstring(whole: String, range: NSRange) -> String? { if range.location != NSNotFound && range.length != 0 { return (whole as NSString).substringWithRange(range) } else { return nil } } public struct RegexMatch { private let sourceString: String private let cocoaMatch: NSTextCheckingResult internal init(cocoaMatch: NSTextCheckingResult, inString source: String) { self.sourceString = source self.cocoaMatch = cocoaMatch } public var range: NSRange { get { return cocoaMatch.range } } public var entireMatch: String? { get { return safeSubstring(sourceString, range: cocoaMatch.range) } } public var subgroupCount: Int { get { return cocoaMatch.numberOfRanges - 1 } } public func subgroupRangeAtIndex(index: Int) -> NSRange? { return cocoaMatch.rangeAtIndex(index + 1) } public func subgroupMatchAtIndex(index: Int) -> String? { let range = cocoaMatch.rangeAtIndex(index + 1) return safeSubstring(sourceString, range: range) } } ================================================ FILE: Pod/Classes/Extend/SwiftRegex/Regex.swift ================================================ // // Regex.swift // Cupertino // // Created by William Kent on 2/19/15. // Copyright (c) 2015 William Kent. All rights reserved. // import Foundation private func convertRange(range: NSRange, relativeToString string: String) -> Range { let start = string.startIndex.advancedBy(range.location) let end = string.startIndex.advancedBy(NSMaxRange(range)) return Range(start: start, end: end) } public enum RegexFlags { case CaseInsensitive case AllowCommentsAndWhitespace case IgnoreMetacharacters case DotMatchesLineSeparators case AnchorsMatchLines case UseUnicodeWordBoundaries } public struct Regex { public let pattern: String public var options: Set public init(_ pattern: String, options: Set) { self.pattern = pattern self.options = options } public init(_ pattern: String) { self.pattern = pattern self.options = [] } private var matcherOptions: NSRegularExpressionOptions { get { var opts: NSRegularExpressionOptions = NSRegularExpressionOptions.CaseInsensitive if options.contains(.CaseInsensitive) { opts = NSRegularExpressionOptions.CaseInsensitive } if options.contains(.AllowCommentsAndWhitespace) { opts = NSRegularExpressionOptions.AllowCommentsAndWhitespace } if options.contains(.IgnoreMetacharacters) { opts = NSRegularExpressionOptions.IgnoreMetacharacters } if options.contains(.DotMatchesLineSeparators) { opts = NSRegularExpressionOptions.DotMatchesLineSeparators } if options.contains(.AnchorsMatchLines) { opts = NSRegularExpressionOptions.AnchorsMatchLines } if options.contains(.UseUnicodeWordBoundaries) { opts = NSRegularExpressionOptions.UseUnicodeWordBoundaries } return opts } } private var matcher: NSRegularExpression? { get { return try? NSRegularExpression(pattern: self.pattern, options: self.matcherOptions) } } public func test(string: String) -> Bool? { return test(string, options: []) } public func test(string: String, options: NSMatchingOptions) -> Bool? { // This function returns true if the regex matches, false if the regex does // not match, or nil if there is a syntax error in the regex itself. if let matcher = matcher { return matcher.numberOfMatchesInString(string, options: options, range: NSMakeRange(0, string.characters.count)) != 0 } else { return nil } } public func match(string: String) -> [RegexMatch]? { return match(string, options: []) } public func match(string: String, options: NSMatchingOptions) -> [RegexMatch]? { if let matcher = matcher { let cocoaMatches = matcher.matchesInString(string, options: options, range: NSMakeRange(0, string.characters.count)) var retval = [RegexMatch]() for match: AnyObject in cocoaMatches { if let match = match as? NSTextCheckingResult { retval.append(RegexMatch(cocoaMatch: match, inString: string)) } } return retval } else { return nil } } public func match(string: String, options: NSMatchingOptions, startPosition: Int) -> [RegexMatch]? { if let matcher = matcher { let cocoaMatches = matcher.matchesInString(string, options: options, range: NSMakeRange(startPosition, string.characters.count - startPosition)) var retval = [RegexMatch]() for match: AnyObject in cocoaMatches { if let match = match as? NSTextCheckingResult { retval.append(RegexMatch(cocoaMatch: match, inString: string)) } } return retval } else { return nil } } public func replace(string: String, withTemplate template: String) -> String? { return replace(string, options: [], withTemplate: template) } public func replace(string: String, options: NSMatchingOptions, withTemplate template: String) -> String? { if let matcher = matcher { let workString = NSMutableString(string: string) matcher.replaceMatchesInString(workString, options: options, range: NSMakeRange(0, workString.length), withTemplate: template) let retval = workString as NSString return String(retval) } return nil } public func replace(string: String, withBlock block: (RegexMatch) -> String) -> String? { return replace(string, options: [], withBlock: block) } public func replace(string: String, options: NSMatchingOptions, withBlock block: (RegexMatch) -> String) -> String? { if let matches = match(string, options: options) { var replacements: [(NSRange, String)] = [] for match in matches { let replacedSubstring = block(match) replacements.append((match.range, replacedSubstring)) } // Sort the replacements in order of location, then reverse it. // By applying the replacements in right-to-left order, I avoid having // to recalculate all the indices when a replacement changes the length // of the replaced substring. replacements.sortInPlace({ (lhs, rhs) -> Bool in let (leftRange, _) = lhs let (rightRange, _) = rhs return leftRange.location < rightRange.location }) replacements = Array(replacements.reverse()) var retval = string for pair in replacements { let (range, substring) = pair retval.replaceRange(convertRange(range, relativeToString: retval), with: substring) } return retval } else { return nil } } } extension Regex: StringLiteralConvertible { public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType public typealias UnicodeScalarLiteralType = UnicodeScalar public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) { self.pattern = "\(value)" self.options = [] } public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) { self.pattern = value self.options = [] } public init(stringLiteral value: StringLiteralType) { self.pattern = value self.options = [] } } ================================================ FILE: Pod/Classes/Extend/URLManager/UIViewController+URLManage.h ================================================ // // UIViewController+URLManage.h // rssreader // // Created by zhuchao on 15/2/11. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import @interface UIViewController (URLManage) @property(nonatomic,retain)NSURL *originUrl; @property(nonatomic,retain)NSString *path; @property(nonatomic,retain)NSDictionary *params; @property(nonatomic,retain)NSDictionary *dictQuery; + (UIViewController *)initFromString:(NSString *)aString fromConfig:(NSDictionary *)config; + (UIViewController *)initFromURL:(NSURL *)url fromConfig:(NSDictionary *)config; + (UIViewController *)initFromString:(NSString *)aString withQuery:(NSDictionary *)query fromConfig:(NSDictionary *)config; + (UIViewController *)initFromURL:(NSURL *)url withQuery:(NSDictionary *)query fromConfig:(NSDictionary *)config; @end @interface NSObject (URLManage) + (Class)classFromString:(NSString *)className; + (NSObject *)objectFromString:(NSString *)className; @end ================================================ FILE: Pod/Classes/Extend/URLManager/UIViewController+URLManage.m ================================================ // // UIViewController+URLManage.m // rssreader // // Created by zhuchao on 15/2/11. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import "UIViewController+URLManage.h" #import static char URLoriginUrl; static char URLpath; static char URLparams; static char URLdictQuery; @implementation UIViewController (URLManage) -(void)setOriginUrl:(NSURL *)originUrl{ [self willChangeValueForKey:@"originUrl"]; objc_setAssociatedObject(self, &URLoriginUrl, originUrl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"originUrl"]; } -(NSURL *)originUrl { return objc_getAssociatedObject(self, &URLoriginUrl); } -(NSString *)path { return objc_getAssociatedObject(self, &URLpath); } -(void)setPath:(NSURL *)path{ [self willChangeValueForKey:@"path"]; objc_setAssociatedObject(self, &URLpath, path, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"path"]; } -(NSDictionary *)params { return objc_getAssociatedObject(self, &URLparams); } -(void)setParams:(NSDictionary *)params{ [self willChangeValueForKey:@"params"]; objc_setAssociatedObject(self, &URLparams, params, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"params"]; } -(NSDictionary *)dictQuery { return objc_getAssociatedObject(self, &URLdictQuery); } -(void)setDictQuery:(NSDictionary *)dictQuery{ [self willChangeValueForKey:@"dictQuery"]; objc_setAssociatedObject(self, &URLdictQuery, dictQuery, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"dictQuery"]; } -(void)open:(NSURL *)url withQuery:(NSDictionary *)dict{ self.path = [url path]; self.originUrl = url; self.dictQuery = dict; NSArray *components = [[url query] componentsSeparatedByString:@"&"]; NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; for (NSString *component in components) { NSArray *subcomponents = [component componentsSeparatedByString:@"="]; [parameters setObject:[[subcomponents objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[subcomponents objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; } self.params = parameters; } + (UIViewController *)initFromString:(NSString *)aString fromConfig:(NSDictionary *)config{ return [UIViewController initFromURL:[NSURL URLWithString:aString] withQuery:nil fromConfig:config]; } + (UIViewController *)initFromURL:(NSURL *)url fromConfig:(NSDictionary *)config{ return [UIViewController initFromURL:url withQuery:nil fromConfig:config]; } + (UIViewController *)initFromString:(NSString *)aString withQuery:(NSDictionary *)query fromConfig:(NSDictionary *)config{ return [UIViewController initFromURL:[NSURL URLWithString:aString] withQuery:query fromConfig:config] ; } + (UIViewController *)initFromURL:(NSURL *)url withQuery:(NSDictionary *)query fromConfig:(NSDictionary *)config { UIViewController* scene = nil; NSString *home; if(url.path ==nil){ home = [NSString stringWithFormat:@"%@://%@", url.scheme, url.host]; }else{ home = [NSString stringWithFormat:@"%@://%@%@", url.scheme, url.host,url.path]; } if([config.allKeys containsObject:url.scheme]){ id cgf = [config objectForKey:url.scheme]; Class class = nil; if([cgf isKindOfClass:[NSString class]]){ class = [NSObject classFromString:cgf]; }else if([cgf isKindOfClass:[NSDictionary class]]){ NSDictionary *dict = (NSDictionary *)cgf; if([dict.allKeys containsObject:home]){ class = [NSObject classFromString:[dict objectForKey:home]]; }else{ class = [NSObject classFromString:url.host]; } } if(class !=nil){ scene = [[class alloc]init]; if([scene respondsToSelector:@selector(open:withQuery:)]){ [scene open:url withQuery:query]; } } }else if([query objectForKey:@"openURL"] || [url.scheme hasPrefix:@"http"]){ [[UIApplication sharedApplication] openURL:url]; } return scene; } @end @implementation NSObject (URLManage) + (Class)classFromString:(NSString *)className { NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; NSString *classStringName = [NSString stringWithFormat:@"%@.%@",appName, className]; return NSClassFromString(classStringName); } + (NSObject *)objectFromString:(NSString *)className{ Class clazz = [NSObject classFromString:className]; return [[clazz alloc]init]; } @end ================================================ FILE: Pod/Classes/Extend/URLManager/URLManager.h ================================================ // // URLManager.h // rssreader // // Created by zhuchao on 15/3/6. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import "URLNavigation.h" #import "UIViewController+URLManage.h" @interface URLManager : NSObject @property(nonatomic,retain)NSDictionary *config; + (URLManager *)shareInstance; + (void)loadConfigFromPlist:(NSString *)plistPath; + (void)pushURLString:(NSString *)urlString animated:(BOOL)animated; + (void)pushURLString:(NSString *)urlString animated:(BOOL)animated replace:(BOOL)replace; + (void)pushURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated; + (void)pushURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated replace:(BOOL)replace; + (void)pushURL:(NSURL *)url animated:(BOOL)animated; + (void)pushURL:(NSURL *)url animated:(BOOL)animated replace:(BOOL)replace; + (void)pushURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated; + (void)pushURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated replace:(BOOL)replace; + (void)presentURL:(NSURL *)url animated:(BOOL)animated; + (void)presentURLString:(NSString *)urlString animated:(BOOL)animated; + (void)presentURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated; + (void)presentURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated; + (void)presentURL:(NSURL *)url animated:(BOOL)animated withNavigationClass:(Class)clazz; + (void)presentURLString:(NSString *)urlString animated:(BOOL)animated withNavigationClass:(Class)clazz; + (void)presentURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated withNavigationClass:(Class)clazz; + (void)presentURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated withNavigationClass:(Class)clazz; @end ================================================ FILE: Pod/Classes/Extend/URLManager/URLManager.m ================================================ // // URLManager.m // rssreader // // Created by zhuchao on 15/3/6. // Copyright (c) 2015年 zhuchao. All rights reserved. // #import "URLManager.h" @implementation URLManager + (URLManager *)shareInstance{ static dispatch_once_t predicate = 0; static id sharedInstance = nil; dispatch_once(&predicate, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } +(void)loadConfigFromPlist:(NSString *)plistPath{ [URLManager shareInstance].config = [NSDictionary dictionaryWithContentsOfFile:plistPath]; } + (void)pushURLString:(NSString *)urlString animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromString:urlString fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated]; } + (void)pushURL:(NSURL *)url animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromURL:url fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated]; } + (void)pushURLString:(NSString *)urlString animated:(BOOL)animated replace:(BOOL)replace{ UIViewController *viewController = [UIViewController initFromString:urlString fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:YES replace:replace]; } + (void)pushURL:(NSURL *)url animated:(BOOL)animated replace:(BOOL)replace{ UIViewController *viewController = [UIViewController initFromURL:url fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated replace:replace]; } + (void)presentURLString:(NSString *)urlString animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromString:urlString fromConfig:[URLManager shareInstance].config]; [URLNavigation presentViewController:viewController animated:animated]; } + (void)presentURL:(NSURL *)url animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromURL:url fromConfig:[URLManager shareInstance].config]; [URLNavigation presentViewController:viewController animated:animated]; } + (void)presentURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromString:urlString withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation presentViewController:viewController animated:animated]; } + (void)presentURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromURL:url withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation presentViewController:viewController animated:animated]; } + (void)pushURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromURL:url withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated]; } + (void)pushURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated replace:(BOOL)replace{ UIViewController *viewController = [UIViewController initFromURL:url withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated replace:replace]; } + (void)pushURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated{ UIViewController *viewController = [UIViewController initFromString:urlString withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated]; } + (void)pushURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated replace:(BOOL)replace{ UIViewController *viewController = [UIViewController initFromString:urlString withQuery:query fromConfig:[URLManager shareInstance].config]; [URLNavigation pushViewController:viewController animated:animated replace:replace]; } + (void)presentURL:(NSURL *)url animated:(BOOL)animated withNavigationClass:(Class)clazz{ UIViewController *viewController = [UIViewController initFromURL:url fromConfig:[URLManager shareInstance].config]; UINavigationController *nav = [[clazz alloc]initWithRootViewController:viewController]; [URLNavigation presentViewController:nav animated:animated]; } + (void)presentURL:(NSURL *)url query:(NSDictionary *)query animated:(BOOL)animated withNavigationClass:(Class)clazz{ UIViewController *viewController = [UIViewController initFromURL:url withQuery:query fromConfig:[URLManager shareInstance].config]; UINavigationController *nav = [[clazz alloc]initWithRootViewController:viewController]; [URLNavigation presentViewController:nav animated:animated]; } + (void)presentURLString:(NSString *)urlString animated:(BOOL)animated withNavigationClass:(Class)clazz{ UIViewController *viewController = [UIViewController initFromString:urlString fromConfig:[URLManager shareInstance].config]; if ([clazz isSubclassOfClass:[UINavigationController class]]) { UINavigationController *nav = [[clazz alloc]initWithRootViewController:viewController]; [URLNavigation presentViewController:nav animated:animated]; } } + (void)presentURLString:(NSString *)urlString query:(NSDictionary *)query animated:(BOOL)animated withNavigationClass:(Class)clazz{ UIViewController *viewController = [UIViewController initFromString:urlString withQuery:query fromConfig:[URLManager shareInstance].config]; if ([clazz isSubclassOfClass:[UINavigationController class]]) { UINavigationController *nav = [[clazz alloc]initWithRootViewController:viewController]; [URLNavigation presentViewController:nav animated:animated]; } } @end ================================================ FILE: Pod/Classes/Extend/URLManager/URLNavigation.h ================================================ // // HKNavigation.h // Hoko // // Created by Hoko, S.A. on 23/07/14. // Copyright (c) 2015 Hoko, S.A. All rights reserved. // #import /** * HKNavigation is a helper class which allows you to push, present and set view * controllers taking basis on your view controller hierararchy. This only takes * account UITabBarControllers, UINavigationControllers and Modal View Controllers * in order to traverse the hierarchy. * * WARNING: If you have custom navigation you should use your own methods to push and present * view controllers, this is only a helper class for common navigation based on Apple's default * view controller hierarchies. * */ @interface URLNavigation : NSObject /** * setRootViewController: changes the root view controller of the AppDelegate's window. * * @param viewController Your view controller (or hierarchy of). */ + (void)setRootViewController:(UIViewController *)viewController; /** * pushViewController:animated: pushes a view controller inside a navigation controller. * If your viewController is a navigation controller it replaces the rootViewController, * otherwise it tries to push the viewController if the current view controller is a * UINavigationController, otherwise it creates it before pushing. * * @param viewController Your view controller. * @param animated If you choose to animate the pushing of the viewController. */ + (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; /** * pushViewController:animated:replace: pushes a view controller inside a navigation controller. * If your viewController is a navigation controller it replaces the rootViewController, * otherwise it tries to push the viewController if the current view controller is a * UINavigationController, otherwise it creates it before pushing. * The replace parameter locates the current view controller and replaces it if it is of * the same class as the viewController parameter being passed, this exists to avoid * two of the same view controller in a row. * * @param viewController Your view controller. * @param animated If you choose to animate the pushing of the viewController. */ + (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated replace:(BOOL)replace; /** * presentViewController:animated: presents a view controller as a modal view controller, * taking basis on the current view controller to present from. * * @param viewController Your view controller. * @param animated If you choose to animate the presenting of the viewController. */ + (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated; +(void)dismissCurrentAnimated:(BOOL)animated; +(UIViewController*)currentViewController; +(UINavigationController*)currentNavigationViewController; @end ================================================ FILE: Pod/Classes/Extend/URLManager/URLNavigation.m ================================================ // // HKObserver.m // Hoko // // Created by Hoko, S.A. on 23/07/14. // Copyright (c) 2015 Hoko, S.A. All rights reserved. // #import "URLNavigation.h" @implementation URLNavigation #pragma mark - Singleton + (instancetype)shareInstance{ static dispatch_once_t predicate = 0; static id sharedInstance = nil; dispatch_once(&predicate, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } #pragma mark - Public Method + (void)setRootViewController:(UIViewController *)viewController { [URLNavigation shareInstance].applicationDelegate.window.rootViewController = viewController; } + (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { [self pushViewController:viewController animated:animated replace:NO]; } + (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated replace:(BOOL)replace { // Check if viewController is a UINavigationController if([viewController isKindOfClass:[UINavigationController class]]) [URLNavigation setRootViewController:viewController]; else { // Check if a UINavigationController exists in the view controllers stack. UINavigationController *navigationController = [URLNavigation shareInstance].currentNavigationViewController; if (navigationController) { // In case it should replace, look for the last UIViewController on the UINavigationController, if it's of the same class, replace it with a new one. if (replace && [navigationController.viewControllers.lastObject isKindOfClass:[viewController class]]) { NSArray *viewControllers = [navigationController.viewControllers subarrayWithRange:NSMakeRange(0, navigationController.viewControllers.count-1)]; [navigationController setViewControllers:[viewControllers arrayByAddingObject:viewController] animated:animated]; } else { // Otherwise just push the new viewController [navigationController pushViewController:viewController animated:animated]; } } else { // Create a new UINavigationController to use with the viewController navigationController = [[UINavigationController alloc]initWithRootViewController:viewController]; [URLNavigation shareInstance].applicationDelegate.window.rootViewController = navigationController; } } } + (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated { // Look for the currentViewController UIViewController *currentViewController = [[URLNavigation shareInstance] currentViewController]; if (currentViewController) { // Present viewController from currentViewcontroller [currentViewController presentViewController:viewController animated:animated completion:nil]; } else { // Otherwise set the window rootViewController [URLNavigation shareInstance].applicationDelegate.window.rootViewController = viewController; } } +(void)dismissCurrentAnimated:(BOOL)animated{ UIViewController *currentViewController = [[URLNavigation shareInstance] currentViewController]; if(currentViewController){ if(currentViewController.navigationController){ if(currentViewController.navigationController.viewControllers.count == 1){ if(currentViewController.presentingViewController){ [currentViewController dismissViewControllerAnimated:animated completion:nil]; } }else{ [currentViewController.navigationController popViewControllerAnimated:animated]; } }else if(currentViewController.presentingViewController){ [currentViewController dismissViewControllerAnimated:animated completion:nil]; } } } #pragma mark - Private Methods - (id)applicationDelegate { return [UIApplication sharedApplication].delegate; } +(UIViewController*)currentViewController{ return [[URLNavigation shareInstance] currentViewController]; } - (UIViewController*)currentViewController { UIViewController* rootViewController = self.applicationDelegate.window.rootViewController; return [self currentViewControllerFrom:rootViewController]; } +(UIViewController*)currentNavigationViewController{ return [[URLNavigation shareInstance] currentNavigationViewController]; } - (UINavigationController*)currentNavigationViewController { UIViewController* currentViewController = self.currentViewController; return currentViewController.navigationController; } - (UIViewController*)currentViewControllerFrom:(UIViewController*)viewController { if ([viewController isKindOfClass:[UINavigationController class]]) { UINavigationController* navigationController = (UINavigationController *)viewController; return [self currentViewControllerFrom:navigationController.viewControllers.lastObject]; } else if([viewController isKindOfClass:[UITabBarController class]]) { UITabBarController* tabBarController = (UITabBarController *)viewController; return [self currentViewControllerFrom:tabBarController.selectedViewController]; } else if(viewController.presentedViewController != nil) { return [self currentViewControllerFrom:viewController.presentedViewController]; } else { return viewController; } } @end ================================================ FILE: Pod/Classes/Private/Gumbo/attribute.c ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #include "attribute.h" #include #include #include #include #include "util.h" struct GumboInternalParser; GumboAttribute* gumbo_get_attribute( const GumboVector* attributes, const char* name) { for (int i = 0; i < attributes->length; ++i) { GumboAttribute* attr = attributes->data[i]; if (!strcasecmp(attr->name, name)) { return attr; } } return NULL; } void gumbo_destroy_attribute( struct GumboInternalParser* parser, GumboAttribute* attribute) { gumbo_parser_deallocate(parser, (void*) attribute->name); gumbo_parser_deallocate(parser, (void*) attribute->value); gumbo_parser_deallocate(parser, (void*) attribute); } ================================================ FILE: Pod/Classes/Private/Gumbo/attribute.h ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #ifndef GUMBO_ATTRIBUTE_H_ #define GUMBO_ATTRIBUTE_H_ #include "gumbo.h" #ifdef __cplusplus extern "C" { #endif struct GumboInternalParser; // Release the memory used for an GumboAttribute, including the attribute // itself. void gumbo_destroy_attribute( struct GumboInternalParser* parser, GumboAttribute* attribute); #ifdef __cplusplus } #endif #endif // GUMBO_ATTRIBUTE_H_ ================================================ FILE: Pod/Classes/Private/Gumbo/char_ref.c ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #include "char_ref.h" #include #include #include #include // Only for debug assertions at present. #include "error.h" #include "string_piece.h" #include "utf8.h" #include "util.h" struct GumboInternalParser; const int kGumboNoChar = -1; // Table of named character entities, and functions for looking them up. // http://www.whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html // // TODO(jdtang): I'd thought of using more efficient means of this, eg. binary // searching the table (which can only be done if we know for sure that there's // enough room in the buffer for our memcmps, otherwise we need to fall back on // linear search) or compiling the list of named entities to a Ragel state // machine. But I'll start with the simple approach and optimize only if // profiling calls for it. The one concession to efficiency is to store the // length of the entity with it, so that we don't need to run a strlen to detect // potential buffer overflows. typedef struct { const char* name; size_t length; OneOrTwoCodepoints codepoints; } NamedCharRef; #define CHAR_REF(name, codepoint) { name, sizeof(name) - 1, { codepoint, -1 } } #define MULTI_CHAR_REF(name, code_point, code_point2) \ { name, sizeof(name) - 1, { code_point, code_point2 } } // Versions with the semicolon must come before versions without the semicolon, // otherwise they'll match the invalid name first and record a parse error. // TODO(jdtang): Replace with a FSM that'll do longest-match-first and probably // give better performance besides. static const NamedCharRef kNamedEntities[] = { CHAR_REF("AElig", 0xc6), CHAR_REF("AMP;", 0x26), CHAR_REF("AMP", 0x26), CHAR_REF("Aacute;", 0xc1), CHAR_REF("Aacute", 0xc1), CHAR_REF("Abreve;", 0x0102), CHAR_REF("Acirc;", 0xc2), CHAR_REF("Acirc", 0xc2), CHAR_REF("Acy;", 0x0410), CHAR_REF("Afr;", 0x0001d504), CHAR_REF("Agrave", 0xc0), CHAR_REF("Agrave;", 0xc0), CHAR_REF("Alpha;", 0x0391), CHAR_REF("Amacr;", 0x0100), CHAR_REF("And;", 0x2a53), CHAR_REF("Aogon;", 0x0104), CHAR_REF("Aopf;", 0x0001d538), CHAR_REF("ApplyFunction;", 0x2061), CHAR_REF("Aring;", 0xc5), CHAR_REF("Aring", 0xc5), CHAR_REF("Ascr;", 0x0001d49c), CHAR_REF("Assign;", 0x2254), CHAR_REF("Atilde;", 0xc3), CHAR_REF("Atilde", 0xc3), CHAR_REF("Auml;", 0xc4), CHAR_REF("Auml", 0xc4), CHAR_REF("Backslash;", 0x2216), CHAR_REF("Barv;", 0x2ae7), CHAR_REF("Barwed;", 0x2306), CHAR_REF("Bcy;", 0x0411), CHAR_REF("Because;", 0x2235), CHAR_REF("Bernoullis;", 0x212c), CHAR_REF("Beta;", 0x0392), CHAR_REF("Bfr;", 0x0001d505), CHAR_REF("Bopf;", 0x0001d539), CHAR_REF("Breve;", 0x02d8), CHAR_REF("Bscr;", 0x212c), CHAR_REF("Bumpeq;", 0x224e), CHAR_REF("CHcy;", 0x0427), CHAR_REF("COPY;", 0xa9), CHAR_REF("COPY", 0xa9), CHAR_REF("Cacute;", 0x0106), CHAR_REF("Cap;", 0x22d2), CHAR_REF("CapitalDifferentialD;", 0x2145), CHAR_REF("Cayleys;", 0x212d), CHAR_REF("Ccaron;", 0x010c), CHAR_REF("Ccedil;", 0xc7), CHAR_REF("Ccedil", 0xc7), CHAR_REF("Ccirc;", 0x0108), CHAR_REF("Cconint;", 0x2230), CHAR_REF("Cdot;", 0x010a), CHAR_REF("Cedilla;", 0xb8), CHAR_REF("CenterDot;", 0xb7), CHAR_REF("Cfr;", 0x212d), CHAR_REF("Chi;", 0x03a7), CHAR_REF("CircleDot;", 0x2299), CHAR_REF("CircleMinus;", 0x2296), CHAR_REF("CirclePlus;", 0x2295), CHAR_REF("CircleTimes;", 0x2297), CHAR_REF("ClockwiseContourIntegral;", 0x2232), CHAR_REF("CloseCurlyDoubleQuote;", 0x201d), CHAR_REF("CloseCurlyQuote;", 0x2019), CHAR_REF("Colon;", 0x2237), CHAR_REF("Colone;", 0x2a74), CHAR_REF("Congruent;", 0x2261), CHAR_REF("Conint;", 0x222f), CHAR_REF("ContourIntegral;", 0x222e), CHAR_REF("Copf;", 0x2102), CHAR_REF("Coproduct;", 0x2210), CHAR_REF("CounterClockwiseContourIntegral;", 0x2233), CHAR_REF("Cross;", 0x2a2f), CHAR_REF("Cscr;", 0x0001d49e), CHAR_REF("Cup;", 0x22d3), CHAR_REF("CupCap;", 0x224d), CHAR_REF("DD;", 0x2145), CHAR_REF("DDotrahd;", 0x2911), CHAR_REF("DJcy;", 0x0402), CHAR_REF("DScy;", 0x0405), CHAR_REF("DZcy;", 0x040f), CHAR_REF("Dagger;", 0x2021), CHAR_REF("Darr;", 0x21a1), CHAR_REF("Dashv;", 0x2ae4), CHAR_REF("Dcaron;", 0x010e), CHAR_REF("Dcy;", 0x0414), CHAR_REF("Del;", 0x2207), CHAR_REF("Delta;", 0x0394), CHAR_REF("Dfr;", 0x0001d507), CHAR_REF("DiacriticalAcute;", 0xb4), CHAR_REF("DiacriticalDot;", 0x02d9), CHAR_REF("DiacriticalDoubleAcute;", 0x02dd), CHAR_REF("DiacriticalGrave;", 0x60), CHAR_REF("DiacriticalTilde;", 0x02dc), CHAR_REF("Diamond;", 0x22c4), CHAR_REF("DifferentialD;", 0x2146), CHAR_REF("Dopf;", 0x0001d53b), CHAR_REF("Dot;", 0xa8), CHAR_REF("DotDot;", 0x20dc), CHAR_REF("DotEqual;", 0x2250), CHAR_REF("DoubleContourIntegral;", 0x222f), CHAR_REF("DoubleDot;", 0xa8), CHAR_REF("DoubleDownArrow;", 0x21d3), CHAR_REF("DoubleLeftArrow;", 0x21d0), CHAR_REF("DoubleLeftRightArrow;", 0x21d4), CHAR_REF("DoubleLeftTee;", 0x2ae4), CHAR_REF("DoubleLongLeftArrow;", 0x27f8), CHAR_REF("DoubleLongLeftRightArrow;", 0x27fa), CHAR_REF("DoubleLongRightArrow;", 0x27f9), CHAR_REF("DoubleRightArrow;", 0x21d2), CHAR_REF("DoubleRightTee;", 0x22a8), CHAR_REF("DoubleUpArrow;", 0x21d1), CHAR_REF("DoubleUpDownArrow;", 0x21d5), CHAR_REF("DoubleVerticalBar;", 0x2225), CHAR_REF("DownArrow;", 0x2193), CHAR_REF("DownArrowBar;", 0x2913), CHAR_REF("DownArrowUpArrow;", 0x21f5), CHAR_REF("DownBreve;", 0x0311), CHAR_REF("DownLeftRightVector;", 0x2950), CHAR_REF("DownLeftTeeVector;", 0x295e), CHAR_REF("DownLeftVector;", 0x21bd), CHAR_REF("DownLeftVectorBar;", 0x2956), CHAR_REF("DownRightTeeVector;", 0x295f), CHAR_REF("DownRightVector;", 0x21c1), CHAR_REF("DownRightVectorBar;", 0x2957), CHAR_REF("DownTee;", 0x22a4), CHAR_REF("DownTeeArrow;", 0x21a7), CHAR_REF("Downarrow;", 0x21d3), CHAR_REF("Dscr;", 0x0001d49f), CHAR_REF("Dstrok;", 0x0110), CHAR_REF("ENG;", 0x014a), CHAR_REF("ETH;", 0xd0), CHAR_REF("ETH", 0xd0), CHAR_REF("Eacute;", 0xc9), CHAR_REF("Eacute", 0xc9), CHAR_REF("Ecaron;", 0x011a), CHAR_REF("Ecirc;", 0xca), CHAR_REF("Ecirc", 0xca), CHAR_REF("Ecy;", 0x042d), CHAR_REF("Edot;", 0x0116), CHAR_REF("Efr;", 0x0001d508), CHAR_REF("Egrave;", 0xc8), CHAR_REF("Egrave", 0xc8), CHAR_REF("Element;", 0x2208), CHAR_REF("Emacr;", 0x0112), CHAR_REF("EmptySmallSquare;", 0x25fb), CHAR_REF("EmptyVerySmallSquare;", 0x25ab), CHAR_REF("Eogon;", 0x0118), CHAR_REF("Eopf;", 0x0001d53c), CHAR_REF("Epsilon;", 0x0395), CHAR_REF("Equal;", 0x2a75), CHAR_REF("EqualTilde;", 0x2242), CHAR_REF("Equilibrium;", 0x21cc), CHAR_REF("Escr;", 0x2130), CHAR_REF("Esim;", 0x2a73), CHAR_REF("Eta;", 0x0397), CHAR_REF("Euml;", 0xcb), CHAR_REF("Euml", 0xcb), CHAR_REF("Exists;", 0x2203), CHAR_REF("ExponentialE;", 0x2147), CHAR_REF("Fcy;", 0x0424), CHAR_REF("Ffr;", 0x0001d509), CHAR_REF("FilledSmallSquare;", 0x25fc), CHAR_REF("FilledVerySmallSquare;", 0x25aa), CHAR_REF("Fopf;", 0x0001d53d), CHAR_REF("ForAll;", 0x2200), CHAR_REF("Fouriertrf;", 0x2131), CHAR_REF("Fscr;", 0x2131), CHAR_REF("GJcy;", 0x0403), CHAR_REF("GT;", 0x3e), CHAR_REF("GT", 0x3e), CHAR_REF("Gamma;", 0x0393), CHAR_REF("Gammad;", 0x03dc), CHAR_REF("Gbreve;", 0x011e), CHAR_REF("Gcedil;", 0x0122), CHAR_REF("Gcirc;", 0x011c), CHAR_REF("Gcy;", 0x0413), CHAR_REF("Gdot;", 0x0120), CHAR_REF("Gfr;", 0x0001d50a), CHAR_REF("Gg;", 0x22d9), CHAR_REF("Gopf;", 0x0001d53e), CHAR_REF("GreaterEqual;", 0x2265), CHAR_REF("GreaterEqualLess;", 0x22db), CHAR_REF("GreaterFullEqual;", 0x2267), CHAR_REF("GreaterGreater;", 0x2aa2), CHAR_REF("GreaterLess;", 0x2277), CHAR_REF("GreaterSlantEqual;", 0x2a7e), CHAR_REF("GreaterTilde;", 0x2273), CHAR_REF("Gscr;", 0x0001d4a2), CHAR_REF("Gt;", 0x226b), CHAR_REF("HARDcy;", 0x042a), CHAR_REF("Hacek;", 0x02c7), CHAR_REF("Hat;", 0x5e), CHAR_REF("Hcirc;", 0x0124), CHAR_REF("Hfr;", 0x210c), CHAR_REF("HilbertSpace;", 0x210b), CHAR_REF("Hopf;", 0x210d), CHAR_REF("HorizontalLine;", 0x2500), CHAR_REF("Hscr;", 0x210b), CHAR_REF("Hstrok;", 0x0126), CHAR_REF("HumpDownHump;", 0x224e), CHAR_REF("HumpEqual;", 0x224f), CHAR_REF("IEcy;", 0x0415), CHAR_REF("IJlig;", 0x0132), CHAR_REF("IOcy;", 0x0401), CHAR_REF("Iacute;", 0xcd), CHAR_REF("Iacute", 0xcd), CHAR_REF("Icirc;", 0xce), CHAR_REF("Icirc", 0xce), CHAR_REF("Icy;", 0x0418), CHAR_REF("Idot;", 0x0130), CHAR_REF("Ifr;", 0x2111), CHAR_REF("Igrave;", 0xcc), CHAR_REF("Igrave", 0xcc), CHAR_REF("Im;", 0x2111), CHAR_REF("Imacr;", 0x012a), CHAR_REF("ImaginaryI;", 0x2148), CHAR_REF("Implies;", 0x21d2), CHAR_REF("Int;", 0x222c), CHAR_REF("Integral;", 0x222b), CHAR_REF("Intersection;", 0x22c2), CHAR_REF("InvisibleComma;", 0x2063), CHAR_REF("InvisibleTimes;", 0x2062), CHAR_REF("Iogon;", 0x012e), CHAR_REF("Iopf;", 0x0001d540), CHAR_REF("Iota;", 0x0399), CHAR_REF("Iscr;", 0x2110), CHAR_REF("Itilde;", 0x0128), CHAR_REF("Iukcy;", 0x0406), CHAR_REF("Iuml;", 0xcf), CHAR_REF("Iuml", 0xcf), CHAR_REF("Jcirc;", 0x0134), CHAR_REF("Jcy;", 0x0419), CHAR_REF("Jfr;", 0x0001d50d), CHAR_REF("Jopf;", 0x0001d541), CHAR_REF("Jscr;", 0x0001d4a5), CHAR_REF("Jsercy;", 0x0408), CHAR_REF("Jukcy;", 0x0404), CHAR_REF("KHcy;", 0x0425), CHAR_REF("KJcy;", 0x040c), CHAR_REF("Kappa;", 0x039a), CHAR_REF("Kcedil;", 0x0136), CHAR_REF("Kcy;", 0x041a), CHAR_REF("Kfr;", 0x0001d50e), CHAR_REF("Kopf;", 0x0001d542), CHAR_REF("Kscr;", 0x0001d4a6), CHAR_REF("LJcy;", 0x0409), CHAR_REF("LT;", 0x3c), CHAR_REF("LT", 0x3c), CHAR_REF("Lacute;", 0x0139), CHAR_REF("Lambda;", 0x039b), CHAR_REF("Lang;", 0x27ea), CHAR_REF("Laplacetrf;", 0x2112), CHAR_REF("Larr;", 0x219e), CHAR_REF("Lcaron;", 0x013d), CHAR_REF("Lcedil;", 0x013b), CHAR_REF("Lcy;", 0x041b), CHAR_REF("LeftAngleBracket;", 0x27e8), CHAR_REF("LeftArrow;", 0x2190), CHAR_REF("LeftArrowBar;", 0x21e4), CHAR_REF("LeftArrowRightArrow;", 0x21c6), CHAR_REF("LeftCeiling;", 0x2308), CHAR_REF("LeftDoubleBracket;", 0x27e6), CHAR_REF("LeftDownTeeVector;", 0x2961), CHAR_REF("LeftDownVector;", 0x21c3), CHAR_REF("LeftDownVectorBar;", 0x2959), CHAR_REF("LeftFloor;", 0x230a), CHAR_REF("LeftRightArrow;", 0x2194), CHAR_REF("LeftRightVector;", 0x294e), CHAR_REF("LeftTee;", 0x22a3), CHAR_REF("LeftTeeArrow;", 0x21a4), CHAR_REF("LeftTeeVector;", 0x295a), CHAR_REF("LeftTriangle;", 0x22b2), CHAR_REF("LeftTriangleBar;", 0x29cf), CHAR_REF("LeftTriangleEqual;", 0x22b4), CHAR_REF("LeftUpDownVector;", 0x2951), CHAR_REF("LeftUpTeeVector;", 0x2960), CHAR_REF("LeftUpVector;", 0x21bf), CHAR_REF("LeftUpVectorBar;", 0x2958), CHAR_REF("LeftVector;", 0x21bc), CHAR_REF("LeftVectorBar;", 0x2952), CHAR_REF("Leftarrow;", 0x21d0), CHAR_REF("Leftrightarrow;", 0x21d4), CHAR_REF("LessEqualGreater;", 0x22da), CHAR_REF("LessFullEqual;", 0x2266), CHAR_REF("LessGreater;", 0x2276), CHAR_REF("LessLess;", 0x2aa1), CHAR_REF("LessSlantEqual;", 0x2a7d), CHAR_REF("LessTilde;", 0x2272), CHAR_REF("Lfr;", 0x0001d50f), CHAR_REF("Ll;", 0x22d8), CHAR_REF("Lleftarrow;", 0x21da), CHAR_REF("Lmidot;", 0x013f), CHAR_REF("LongLeftArrow;", 0x27f5), CHAR_REF("LongLeftRightArrow;", 0x27f7), CHAR_REF("LongRightArrow;", 0x27f6), CHAR_REF("Longleftarrow;", 0x27f8), CHAR_REF("Longleftrightarrow;", 0x27fa), CHAR_REF("Longrightarrow;", 0x27f9), CHAR_REF("Lopf;", 0x0001d543), CHAR_REF("LowerLeftArrow;", 0x2199), CHAR_REF("LowerRightArrow;", 0x2198), CHAR_REF("Lscr;", 0x2112), CHAR_REF("Lsh;", 0x21b0), CHAR_REF("Lstrok;", 0x0141), CHAR_REF("Lt;", 0x226a), CHAR_REF("Map;", 0x2905), CHAR_REF("Mcy;", 0x041c), CHAR_REF("MediumSpace;", 0x205f), CHAR_REF("Mellintrf;", 0x2133), CHAR_REF("Mfr;", 0x0001d510), CHAR_REF("MinusPlus;", 0x2213), CHAR_REF("Mopf;", 0x0001d544), CHAR_REF("Mscr;", 0x2133), CHAR_REF("Mu;", 0x039c), CHAR_REF("NJcy;", 0x040a), CHAR_REF("Nacute;", 0x0143), CHAR_REF("Ncaron;", 0x0147), CHAR_REF("Ncedil;", 0x0145), CHAR_REF("Ncy;", 0x041d), CHAR_REF("NegativeMediumSpace;", 0x200b), CHAR_REF("NegativeThickSpace;", 0x200b), CHAR_REF("NegativeThinSpace;", 0x200b), CHAR_REF("NegativeVeryThinSpace;", 0x200b), CHAR_REF("NestedGreaterGreater;", 0x226b), CHAR_REF("NestedLessLess;", 0x226a), CHAR_REF("NewLine;", 0x0a), CHAR_REF("Nfr;", 0x0001d511), CHAR_REF("NoBreak;", 0x2060), CHAR_REF("NonBreakingSpace;", 0xa0), CHAR_REF("Nopf;", 0x2115), CHAR_REF("Not;", 0x2aec), CHAR_REF("NotCongruent;", 0x2262), CHAR_REF("NotCupCap;", 0x226d), CHAR_REF("NotDoubleVerticalBar;", 0x2226), CHAR_REF("NotElement;", 0x2209), CHAR_REF("NotEqual;", 0x2260), MULTI_CHAR_REF("NotEqualTilde;", 0x2242, 0x0338), CHAR_REF("NotExists;", 0x2204), CHAR_REF("NotGreater;", 0x226f), CHAR_REF("NotGreaterEqual;", 0x2271), MULTI_CHAR_REF("NotGreaterFullEqual;", 0x2267, 0x0338), MULTI_CHAR_REF("NotGreaterGreater;", 0x226b, 0x0338), CHAR_REF("NotGreaterLess;", 0x2279), MULTI_CHAR_REF("NotGreaterSlantEqual;", 0x2a7e, 0x0338), CHAR_REF("NotGreaterTilde;", 0x2275), MULTI_CHAR_REF("NotHumpDownHump;", 0x224e, 0x0338), MULTI_CHAR_REF("NotHumpEqual;", 0x224f, 0x0338), CHAR_REF("NotLeftTriangle;", 0x22ea), MULTI_CHAR_REF("NotLeftTriangleBar;", 0x29cf, 0x0338), CHAR_REF("NotLeftTriangleEqual;", 0x22ec), CHAR_REF("NotLess;", 0x226e), CHAR_REF("NotLessEqual;", 0x2270), CHAR_REF("NotLessGreater;", 0x2278), MULTI_CHAR_REF("NotLessLess;", 0x226a, 0x0338), MULTI_CHAR_REF("NotLessSlantEqual;", 0x2a7d, 0x0338), CHAR_REF("NotLessTilde;", 0x2274), MULTI_CHAR_REF("NotNestedGreaterGreater;", 0x2aa2, 0x0338), MULTI_CHAR_REF("NotNestedLessLess;", 0x2aa1, 0x0338), CHAR_REF("NotPrecedes;", 0x2280), MULTI_CHAR_REF("NotPrecedesEqual;", 0x2aaf, 0x0338), CHAR_REF("NotPrecedesSlantEqual;", 0x22e0), CHAR_REF("NotReverseElement;", 0x220c), CHAR_REF("NotRightTriangle;", 0x22eb), MULTI_CHAR_REF("NotRightTriangleBar;", 0x29d0, 0x0338), CHAR_REF("NotRightTriangleEqual;", 0x22ed), MULTI_CHAR_REF("NotSquareSubset;", 0x228f, 0x0338), CHAR_REF("NotSquareSubsetEqual;", 0x22e2), MULTI_CHAR_REF("NotSquareSuperset;", 0x2290, 0x0338), CHAR_REF("NotSquareSupersetEqual;", 0x22e3), MULTI_CHAR_REF("NotSubset;", 0x2282, 0x20d2), CHAR_REF("NotSubsetEqual;", 0x2288), CHAR_REF("NotSucceeds;", 0x2281), MULTI_CHAR_REF("NotSucceedsEqual;", 0x2ab0, 0x0338), CHAR_REF("NotSucceedsSlantEqual;", 0x22e1), MULTI_CHAR_REF("NotSucceedsTilde;", 0x227f, 0x0338), MULTI_CHAR_REF("NotSuperset;", 0x2283, 0x20d2), CHAR_REF("NotSupersetEqual;", 0x2289), CHAR_REF("NotTilde;", 0x2241), CHAR_REF("NotTildeEqual;", 0x2244), CHAR_REF("NotTildeFullEqual;", 0x2247), CHAR_REF("NotTildeTilde;", 0x2249), CHAR_REF("NotVerticalBar;", 0x2224), CHAR_REF("Nscr;", 0x0001d4a9), CHAR_REF("Ntilde;", 0xd1), CHAR_REF("Ntilde", 0xd1), CHAR_REF("Nu;", 0x039d), CHAR_REF("OElig;", 0x0152), CHAR_REF("Oacute;", 0xd3), CHAR_REF("Oacute", 0xd3), CHAR_REF("Ocirc;", 0xd4), CHAR_REF("Ocirc", 0xd4), CHAR_REF("Ocy;", 0x041e), CHAR_REF("Odblac;", 0x0150), CHAR_REF("Ofr;", 0x0001d512), CHAR_REF("Ograve;", 0xd2), CHAR_REF("Ograve", 0xd2), CHAR_REF("Omacr;", 0x014c), CHAR_REF("Omega;", 0x03a9), CHAR_REF("Omicron;", 0x039f), CHAR_REF("Oopf;", 0x0001d546), CHAR_REF("OpenCurlyDoubleQuote;", 0x201c), CHAR_REF("OpenCurlyQuote;", 0x2018), CHAR_REF("Or;", 0x2a54), CHAR_REF("Oscr;", 0x0001d4aa), CHAR_REF("Oslash;", 0xd8), CHAR_REF("Oslash", 0xd8), CHAR_REF("Otilde;", 0xd5), CHAR_REF("Otilde", 0xd5), CHAR_REF("Otimes;", 0x2a37), CHAR_REF("Ouml", 0xd6), CHAR_REF("Ouml;", 0xd6), CHAR_REF("OverBar;", 0x203e), CHAR_REF("OverBrace;", 0x23de), CHAR_REF("OverBracket;", 0x23b4), CHAR_REF("OverParenthesis;", 0x23dc), CHAR_REF("PartialD;", 0x2202), CHAR_REF("Pcy;", 0x041f), CHAR_REF("Pfr;", 0x0001d513), CHAR_REF("Phi;", 0x03a6), CHAR_REF("Pi;", 0x03a0), CHAR_REF("PlusMinus;", 0xb1), CHAR_REF("Poincareplane;", 0x210c), CHAR_REF("Popf;", 0x2119), CHAR_REF("Pr;", 0x2abb), CHAR_REF("Precedes;", 0x227a), CHAR_REF("PrecedesEqual;", 0x2aaf), CHAR_REF("PrecedesSlantEqual;", 0x227c), CHAR_REF("PrecedesTilde;", 0x227e), CHAR_REF("Prime;", 0x2033), CHAR_REF("Product;", 0x220f), CHAR_REF("Proportion;", 0x2237), CHAR_REF("Proportional;", 0x221d), CHAR_REF("Pscr;", 0x0001d4ab), CHAR_REF("Psi;", 0x03a8), CHAR_REF("QUOT;", 0x22), CHAR_REF("QUOT", 0x22), CHAR_REF("Qfr;", 0x0001d514), CHAR_REF("Qopf;", 0x211a), CHAR_REF("Qscr;", 0x0001d4ac), CHAR_REF("RBarr;", 0x2910), CHAR_REF("REG;", 0xae), CHAR_REF("REG", 0xae), CHAR_REF("Racute;", 0x0154), CHAR_REF("Rang;", 0x27eb), CHAR_REF("Rarr;", 0x21a0), CHAR_REF("Rarrtl;", 0x2916), CHAR_REF("Rcaron;", 0x0158), CHAR_REF("Rcedil;", 0x0156), CHAR_REF("Rcy;", 0x0420), CHAR_REF("Re;", 0x211c), CHAR_REF("ReverseElement;", 0x220b), CHAR_REF("ReverseEquilibrium;", 0x21cb), CHAR_REF("ReverseUpEquilibrium;", 0x296f), CHAR_REF("Rfr;", 0x211c), CHAR_REF("Rho;", 0x03a1), CHAR_REF("RightAngleBracket;", 0x27e9), CHAR_REF("RightArrow;", 0x2192), CHAR_REF("RightArrowBar;", 0x21e5), CHAR_REF("RightArrowLeftArrow;", 0x21c4), CHAR_REF("RightCeiling;", 0x2309), CHAR_REF("RightDoubleBracket;", 0x27e7), CHAR_REF("RightDownTeeVector;", 0x295d), CHAR_REF("RightDownVector;", 0x21c2), CHAR_REF("RightDownVectorBar;", 0x2955), CHAR_REF("RightFloor;", 0x230b), CHAR_REF("RightTee;", 0x22a2), CHAR_REF("RightTeeArrow;", 0x21a6), CHAR_REF("RightTeeVector;", 0x295b), CHAR_REF("RightTriangle;", 0x22b3), CHAR_REF("RightTriangleBar;", 0x29d0), CHAR_REF("RightTriangleEqual;", 0x22b5), CHAR_REF("RightUpDownVector;", 0x294f), CHAR_REF("RightUpTeeVector;", 0x295c), CHAR_REF("RightUpVector;", 0x21be), CHAR_REF("RightUpVectorBar;", 0x2954), CHAR_REF("RightVector;", 0x21c0), CHAR_REF("RightVectorBar;", 0x2953), CHAR_REF("Rightarrow;", 0x21d2), CHAR_REF("Ropf;", 0x211d), CHAR_REF("RoundImplies;", 0x2970), CHAR_REF("Rrightarrow;", 0x21db), CHAR_REF("Rscr;", 0x211b), CHAR_REF("Rsh;", 0x21b1), CHAR_REF("RuleDelayed;", 0x29f4), CHAR_REF("SHCHcy;", 0x0429), CHAR_REF("SHcy;", 0x0428), CHAR_REF("SOFTcy;", 0x042c), CHAR_REF("Sacute;", 0x015a), CHAR_REF("Sc;", 0x2abc), CHAR_REF("Scaron;", 0x0160), CHAR_REF("Scedil;", 0x015e), CHAR_REF("Scirc;", 0x015c), CHAR_REF("Scy;", 0x0421), CHAR_REF("Sfr;", 0x0001d516), CHAR_REF("ShortDownArrow;", 0x2193), CHAR_REF("ShortLeftArrow;", 0x2190), CHAR_REF("ShortRightArrow;", 0x2192), CHAR_REF("ShortUpArrow;", 0x2191), CHAR_REF("Sigma;", 0x03a3), CHAR_REF("SmallCircle;", 0x2218), CHAR_REF("Sopf;", 0x0001d54a), CHAR_REF("Sqrt;", 0x221a), CHAR_REF("Square;", 0x25a1), CHAR_REF("SquareIntersection;", 0x2293), CHAR_REF("SquareSubset;", 0x228f), CHAR_REF("SquareSubsetEqual;", 0x2291), CHAR_REF("SquareSuperset;", 0x2290), CHAR_REF("SquareSupersetEqual;", 0x2292), CHAR_REF("SquareUnion;", 0x2294), CHAR_REF("Sscr;", 0x0001d4ae), CHAR_REF("Star;", 0x22c6), CHAR_REF("Sub;", 0x22d0), CHAR_REF("Subset;", 0x22d0), CHAR_REF("SubsetEqual;", 0x2286), CHAR_REF("Succeeds;", 0x227b), CHAR_REF("SucceedsEqual;", 0x2ab0), CHAR_REF("SucceedsSlantEqual;", 0x227d), CHAR_REF("SucceedsTilde;", 0x227f), CHAR_REF("SuchThat;", 0x220b), CHAR_REF("Sum;", 0x2211), CHAR_REF("Sup;", 0x22d1), CHAR_REF("Superset;", 0x2283), CHAR_REF("SupersetEqual;", 0x2287), CHAR_REF("Supset;", 0x22d1), CHAR_REF("THORN;", 0xde), CHAR_REF("THORN", 0xde), CHAR_REF("TRADE;", 0x2122), CHAR_REF("TSHcy;", 0x040b), CHAR_REF("TScy;", 0x0426), CHAR_REF("Tab;", 0x09), CHAR_REF("Tau;", 0x03a4), CHAR_REF("Tcaron;", 0x0164), CHAR_REF("Tcedil;", 0x0162), CHAR_REF("Tcy;", 0x0422), CHAR_REF("Tfr;", 0x0001d517), CHAR_REF("Therefore;", 0x2234), CHAR_REF("Theta;", 0x0398), MULTI_CHAR_REF("ThickSpace;", 0x205f, 0x200a), CHAR_REF("ThinSpace;", 0x2009), CHAR_REF("Tilde;", 0x223c), CHAR_REF("TildeEqual;", 0x2243), CHAR_REF("TildeFullEqual;", 0x2245), CHAR_REF("TildeTilde;", 0x2248), CHAR_REF("Topf;", 0x0001d54b), CHAR_REF("TripleDot;", 0x20db), CHAR_REF("Tscr;", 0x0001d4af), CHAR_REF("Tstrok;", 0x0166), CHAR_REF("Uacute;", 0xda), CHAR_REF("Uacute", 0xda), CHAR_REF("Uarr;", 0x219f), CHAR_REF("Uarrocir;", 0x2949), CHAR_REF("Ubrcy;", 0x040e), CHAR_REF("Ubreve;", 0x016c), CHAR_REF("Ucirc;", 0xdb), CHAR_REF("Ucirc", 0xdb), CHAR_REF("Ucy;", 0x0423), CHAR_REF("Udblac;", 0x0170), CHAR_REF("Ufr;", 0x0001d518), CHAR_REF("Ugrave;", 0xd9), CHAR_REF("Ugrave", 0xd9), CHAR_REF("Umacr;", 0x016a), CHAR_REF("UnderBar;", 0x5f), CHAR_REF("UnderBrace;", 0x23df), CHAR_REF("UnderBracket;", 0x23b5), CHAR_REF("UnderParenthesis;", 0x23dd), CHAR_REF("Union;", 0x22c3), CHAR_REF("UnionPlus;", 0x228e), CHAR_REF("Uogon;", 0x0172), CHAR_REF("Uopf;", 0x0001d54c), CHAR_REF("UpArrow;", 0x2191), CHAR_REF("UpArrowBar;", 0x2912), CHAR_REF("UpArrowDownArrow;", 0x21c5), CHAR_REF("UpDownArrow;", 0x2195), CHAR_REF("UpEquilibrium;", 0x296e), CHAR_REF("UpTee;", 0x22a5), CHAR_REF("UpTeeArrow;", 0x21a5), CHAR_REF("Uparrow;", 0x21d1), CHAR_REF("Updownarrow;", 0x21d5), CHAR_REF("UpperLeftArrow;", 0x2196), CHAR_REF("UpperRightArrow;", 0x2197), CHAR_REF("Upsi;", 0x03d2), CHAR_REF("Upsilon;", 0x03a5), CHAR_REF("Uring;", 0x016e), CHAR_REF("Uscr;", 0x0001d4b0), CHAR_REF("Utilde;", 0x0168), CHAR_REF("Uuml;", 0xdc), CHAR_REF("Uuml", 0xdc), CHAR_REF("VDash;", 0x22ab), CHAR_REF("Vbar;", 0x2aeb), CHAR_REF("Vcy;", 0x0412), CHAR_REF("Vdash;", 0x22a9), CHAR_REF("Vdashl;", 0x2ae6), CHAR_REF("Vee;", 0x22c1), CHAR_REF("Verbar;", 0x2016), CHAR_REF("Vert;", 0x2016), CHAR_REF("VerticalBar;", 0x2223), CHAR_REF("VerticalLine;", 0x7c), CHAR_REF("VerticalSeparator;", 0x2758), CHAR_REF("VerticalTilde;", 0x2240), CHAR_REF("VeryThinSpace;", 0x200a), CHAR_REF("Vfr;", 0x0001d519), CHAR_REF("Vopf;", 0x0001d54d), CHAR_REF("Vscr;", 0x0001d4b1), CHAR_REF("Vvdash;", 0x22aa), CHAR_REF("Wcirc;", 0x0174), CHAR_REF("Wedge;", 0x22c0), CHAR_REF("Wfr;", 0x0001d51a), CHAR_REF("Wopf;", 0x0001d54e), CHAR_REF("Wscr;", 0x0001d4b2), CHAR_REF("Xfr;", 0x0001d51b), CHAR_REF("Xi;", 0x039e), CHAR_REF("Xopf;", 0x0001d54f), CHAR_REF("Xscr;", 0x0001d4b3), CHAR_REF("YAcy;", 0x042f), CHAR_REF("YIcy;", 0x0407), CHAR_REF("YUcy;", 0x042e), CHAR_REF("Yacute", 0xdd), CHAR_REF("Yacute;", 0xdd), CHAR_REF("Ycirc;", 0x0176), CHAR_REF("Ycy;", 0x042b), CHAR_REF("Yfr;", 0x0001d51c), CHAR_REF("Yopf;", 0x0001d550), CHAR_REF("Yscr;", 0x0001d4b4), CHAR_REF("Yuml;", 0x0178), CHAR_REF("ZHcy;", 0x0416), CHAR_REF("Zacute;", 0x0179), CHAR_REF("Zcaron;", 0x017d), CHAR_REF("Zcy;", 0x0417), CHAR_REF("Zdot;", 0x017b), CHAR_REF("ZeroWidthSpace;", 0x200b), CHAR_REF("Zeta;", 0x0396), CHAR_REF("Zfr;", 0x2128), CHAR_REF("Zopf;", 0x2124), CHAR_REF("Zscr;", 0x0001d4b5), CHAR_REF("aacute;", 0xe1), CHAR_REF("aacute", 0xe1), CHAR_REF("abreve;", 0x0103), CHAR_REF("ac;", 0x223e), MULTI_CHAR_REF("acE;", 0x223e, 0x0333), CHAR_REF("acd;", 0x223f), CHAR_REF("acirc;", 0xe2), CHAR_REF("acirc", 0xe2), CHAR_REF("acute;", 0xb4), CHAR_REF("acute", 0xb4), CHAR_REF("acy;", 0x0430), CHAR_REF("aelig;", 0xe6), CHAR_REF("aelig", 0xe6), CHAR_REF("af;", 0x2061), CHAR_REF("afr;", 0x0001d51e), CHAR_REF("agrave;", 0xe0), CHAR_REF("agrave", 0xe0), CHAR_REF("alefsym;", 0x2135), CHAR_REF("aleph;", 0x2135), CHAR_REF("alpha;", 0x03b1), CHAR_REF("amacr;", 0x0101), CHAR_REF("amalg;", 0x2a3f), CHAR_REF("amp;", 0x26), CHAR_REF("amp", 0x26), CHAR_REF("and;", 0x2227), CHAR_REF("andand;", 0x2a55), CHAR_REF("andd;", 0x2a5c), CHAR_REF("andslope;", 0x2a58), CHAR_REF("andv;", 0x2a5a), CHAR_REF("ang;", 0x2220), CHAR_REF("ange;", 0x29a4), CHAR_REF("angle;", 0x2220), CHAR_REF("angmsd;", 0x2221), CHAR_REF("angmsdaa;", 0x29a8), CHAR_REF("angmsdab;", 0x29a9), CHAR_REF("angmsdac;", 0x29aa), CHAR_REF("angmsdad;", 0x29ab), CHAR_REF("angmsdae;", 0x29ac), CHAR_REF("angmsdaf;", 0x29ad), CHAR_REF("angmsdag;", 0x29ae), CHAR_REF("angmsdah;", 0x29af), CHAR_REF("angrt;", 0x221f), CHAR_REF("angrtvb;", 0x22be), CHAR_REF("angrtvbd;", 0x299d), CHAR_REF("angsph;", 0x2222), CHAR_REF("angst;", 0xc5), CHAR_REF("angzarr;", 0x237c), CHAR_REF("aogon;", 0x0105), CHAR_REF("aopf;", 0x0001d552), CHAR_REF("ap;", 0x2248), CHAR_REF("apE;", 0x2a70), CHAR_REF("apacir;", 0x2a6f), CHAR_REF("ape;", 0x224a), CHAR_REF("apid;", 0x224b), CHAR_REF("apos;", 0x27), CHAR_REF("approx;", 0x2248), CHAR_REF("approxeq;", 0x224a), CHAR_REF("aring;", 0xe5), CHAR_REF("aring", 0xe5), CHAR_REF("ascr;", 0x0001d4b6), CHAR_REF("ast;", 0x2a), CHAR_REF("asymp;", 0x2248), CHAR_REF("asympeq;", 0x224d), CHAR_REF("atilde;", 0xe3), CHAR_REF("atilde", 0xe3), CHAR_REF("auml;", 0xe4), CHAR_REF("auml", 0xe4), CHAR_REF("awconint;", 0x2233), CHAR_REF("awint;", 0x2a11), CHAR_REF("bNot;", 0x2aed), CHAR_REF("backcong;", 0x224c), CHAR_REF("backepsilon;", 0x03f6), CHAR_REF("backprime;", 0x2035), CHAR_REF("backsim;", 0x223d), CHAR_REF("backsimeq;", 0x22cd), CHAR_REF("barvee;", 0x22bd), CHAR_REF("barwed;", 0x2305), CHAR_REF("barwedge;", 0x2305), CHAR_REF("bbrk;", 0x23b5), CHAR_REF("bbrktbrk;", 0x23b6), CHAR_REF("bcong;", 0x224c), CHAR_REF("bcy;", 0x0431), CHAR_REF("bdquo;", 0x201e), CHAR_REF("becaus;", 0x2235), CHAR_REF("because;", 0x2235), CHAR_REF("bemptyv;", 0x29b0), CHAR_REF("bepsi;", 0x03f6), CHAR_REF("bernou;", 0x212c), CHAR_REF("beta;", 0x03b2), CHAR_REF("beth;", 0x2136), CHAR_REF("between;", 0x226c), CHAR_REF("bfr;", 0x0001d51f), CHAR_REF("bigcap;", 0x22c2), CHAR_REF("bigcirc;", 0x25ef), CHAR_REF("bigcup;", 0x22c3), CHAR_REF("bigodot;", 0x2a00), CHAR_REF("bigoplus;", 0x2a01), CHAR_REF("bigotimes;", 0x2a02), CHAR_REF("bigsqcup;", 0x2a06), CHAR_REF("bigstar;", 0x2605), CHAR_REF("bigtriangledown;", 0x25bd), CHAR_REF("bigtriangleup;", 0x25b3), CHAR_REF("biguplus;", 0x2a04), CHAR_REF("bigvee;", 0x22c1), CHAR_REF("bigwedge;", 0x22c0), CHAR_REF("bkarow;", 0x290d), CHAR_REF("blacklozenge;", 0x29eb), CHAR_REF("blacksquare;", 0x25aa), CHAR_REF("blacktriangle;", 0x25b4), CHAR_REF("blacktriangledown;", 0x25be), CHAR_REF("blacktriangleleft;", 0x25c2), CHAR_REF("blacktriangleright;", 0x25b8), CHAR_REF("blank;", 0x2423), CHAR_REF("blk12;", 0x2592), CHAR_REF("blk14;", 0x2591), CHAR_REF("blk34;", 0x2593), CHAR_REF("block;", 0x2588), MULTI_CHAR_REF("bne;", 0x3d, 0x20e5), MULTI_CHAR_REF("bnequiv;", 0x2261, 0x20e5), CHAR_REF("bnot;", 0x2310), CHAR_REF("bopf;", 0x0001d553), CHAR_REF("bot;", 0x22a5), CHAR_REF("bottom;", 0x22a5), CHAR_REF("bowtie;", 0x22c8), CHAR_REF("boxDL;", 0x2557), CHAR_REF("boxDR;", 0x2554), CHAR_REF("boxDl;", 0x2556), CHAR_REF("boxDr;", 0x2553), CHAR_REF("boxH;", 0x2550), CHAR_REF("boxHD;", 0x2566), CHAR_REF("boxHU;", 0x2569), CHAR_REF("boxHd;", 0x2564), CHAR_REF("boxHu;", 0x2567), CHAR_REF("boxUL;", 0x255d), CHAR_REF("boxUR;", 0x255a), CHAR_REF("boxUl;", 0x255c), CHAR_REF("boxUr;", 0x2559), CHAR_REF("boxV;", 0x2551), CHAR_REF("boxVH;", 0x256c), CHAR_REF("boxVL;", 0x2563), CHAR_REF("boxVR;", 0x2560), CHAR_REF("boxVh;", 0x256b), CHAR_REF("boxVl;", 0x2562), CHAR_REF("boxVr;", 0x255f), CHAR_REF("boxbox;", 0x29c9), CHAR_REF("boxdL;", 0x2555), CHAR_REF("boxdR;", 0x2552), CHAR_REF("boxdl;", 0x2510), CHAR_REF("boxdr;", 0x250c), CHAR_REF("boxh;", 0x2500), CHAR_REF("boxhD;", 0x2565), CHAR_REF("boxhU;", 0x2568), CHAR_REF("boxhd;", 0x252c), CHAR_REF("boxhu;", 0x2534), CHAR_REF("boxminus;", 0x229f), CHAR_REF("boxplus;", 0x229e), CHAR_REF("boxtimes;", 0x22a0), CHAR_REF("boxuL;", 0x255b), CHAR_REF("boxuR;", 0x2558), CHAR_REF("boxul;", 0x2518), CHAR_REF("boxur;", 0x2514), CHAR_REF("boxv;", 0x2502), CHAR_REF("boxvH;", 0x256a), CHAR_REF("boxvL;", 0x2561), CHAR_REF("boxvR;", 0x255e), CHAR_REF("boxvh;", 0x253c), CHAR_REF("boxvl;", 0x2524), CHAR_REF("boxvr;", 0x251c), CHAR_REF("bprime;", 0x2035), CHAR_REF("breve;", 0x02d8), CHAR_REF("brvbar;", 0xa6), CHAR_REF("brvbar", 0xa6), CHAR_REF("bscr;", 0x0001d4b7), CHAR_REF("bsemi;", 0x204f), CHAR_REF("bsim;", 0x223d), CHAR_REF("bsime;", 0x22cd), CHAR_REF("bsol;", 0x5c), CHAR_REF("bsolb;", 0x29c5), CHAR_REF("bsolhsub;", 0x27c8), CHAR_REF("bull;", 0x2022), CHAR_REF("bullet;", 0x2022), CHAR_REF("bump;", 0x224e), CHAR_REF("bumpE;", 0x2aae), CHAR_REF("bumpe;", 0x224f), CHAR_REF("bumpeq;", 0x224f), CHAR_REF("cacute;", 0x0107), CHAR_REF("cap;", 0x2229), CHAR_REF("capand;", 0x2a44), CHAR_REF("capbrcup;", 0x2a49), CHAR_REF("capcap;", 0x2a4b), CHAR_REF("capcup;", 0x2a47), CHAR_REF("capdot;", 0x2a40), MULTI_CHAR_REF("caps;", 0x2229, 0xfe00), CHAR_REF("caret;", 0x2041), CHAR_REF("caron;", 0x02c7), CHAR_REF("ccaps;", 0x2a4d), CHAR_REF("ccaron;", 0x010d), CHAR_REF("ccedil;", 0xe7), CHAR_REF("ccedil", 0xe7), CHAR_REF("ccirc;", 0x0109), CHAR_REF("ccups;", 0x2a4c), CHAR_REF("ccupssm;", 0x2a50), CHAR_REF("cdot;", 0x010b), CHAR_REF("cedil;", 0xb8), CHAR_REF("cedil", 0xb8), CHAR_REF("cemptyv;", 0x29b2), CHAR_REF("cent;", 0xa2), CHAR_REF("cent", 0xa2), CHAR_REF("centerdot;", 0xb7), CHAR_REF("cfr;", 0x0001d520), CHAR_REF("chcy;", 0x0447), CHAR_REF("check;", 0x2713), CHAR_REF("checkmark;", 0x2713), CHAR_REF("chi;", 0x03c7), CHAR_REF("cir;", 0x25cb), CHAR_REF("cirE;", 0x29c3), CHAR_REF("circ;", 0x02c6), CHAR_REF("circeq;", 0x2257), CHAR_REF("circlearrowleft;", 0x21ba), CHAR_REF("circlearrowright;", 0x21bb), CHAR_REF("circledR;", 0xae), CHAR_REF("circledS;", 0x24c8), CHAR_REF("circledast;", 0x229b), CHAR_REF("circledcirc;", 0x229a), CHAR_REF("circleddash;", 0x229d), CHAR_REF("cire;", 0x2257), CHAR_REF("cirfnint;", 0x2a10), CHAR_REF("cirmid;", 0x2aef), CHAR_REF("cirscir;", 0x29c2), CHAR_REF("clubs;", 0x2663), CHAR_REF("clubsuit;", 0x2663), CHAR_REF("colon;", 0x3a), CHAR_REF("colone;", 0x2254), CHAR_REF("coloneq;", 0x2254), CHAR_REF("comma;", 0x2c), CHAR_REF("commat;", 0x40), CHAR_REF("comp;", 0x2201), CHAR_REF("compfn;", 0x2218), CHAR_REF("complement;", 0x2201), CHAR_REF("complexes;", 0x2102), CHAR_REF("cong;", 0x2245), CHAR_REF("congdot;", 0x2a6d), CHAR_REF("conint;", 0x222e), CHAR_REF("copf;", 0x0001d554), CHAR_REF("coprod;", 0x2210), CHAR_REF("copy;", 0xa9), CHAR_REF("copy", 0xa9), CHAR_REF("copysr;", 0x2117), CHAR_REF("crarr;", 0x21b5), CHAR_REF("cross;", 0x2717), CHAR_REF("cscr;", 0x0001d4b8), CHAR_REF("csub;", 0x2acf), CHAR_REF("csube;", 0x2ad1), CHAR_REF("csup;", 0x2ad0), CHAR_REF("csupe;", 0x2ad2), CHAR_REF("ctdot;", 0x22ef), CHAR_REF("cudarrl;", 0x2938), CHAR_REF("cudarrr;", 0x2935), CHAR_REF("cuepr;", 0x22de), CHAR_REF("cuesc;", 0x22df), CHAR_REF("cularr;", 0x21b6), CHAR_REF("cularrp;", 0x293d), CHAR_REF("cup;", 0x222a), CHAR_REF("cupbrcap;", 0x2a48), CHAR_REF("cupcap;", 0x2a46), CHAR_REF("cupcup;", 0x2a4a), CHAR_REF("cupdot;", 0x228d), CHAR_REF("cupor;", 0x2a45), MULTI_CHAR_REF("cups;", 0x222a, 0xfe00), CHAR_REF("curarr;", 0x21b7), CHAR_REF("curarrm;", 0x293c), CHAR_REF("curlyeqprec;", 0x22de), CHAR_REF("curlyeqsucc;", 0x22df), CHAR_REF("curlyvee;", 0x22ce), CHAR_REF("curlywedge;", 0x22cf), CHAR_REF("curren;", 0xa4), CHAR_REF("curren", 0xa4), CHAR_REF("curvearrowleft;", 0x21b6), CHAR_REF("curvearrowright;", 0x21b7), CHAR_REF("cuvee;", 0x22ce), CHAR_REF("cuwed;", 0x22cf), CHAR_REF("cwconint;", 0x2232), CHAR_REF("cwint;", 0x2231), CHAR_REF("cylcty;", 0x232d), CHAR_REF("dArr;", 0x21d3), CHAR_REF("dHar;", 0x2965), CHAR_REF("dagger;", 0x2020), CHAR_REF("daleth;", 0x2138), CHAR_REF("darr;", 0x2193), CHAR_REF("dash;", 0x2010), CHAR_REF("dashv;", 0x22a3), CHAR_REF("dbkarow;", 0x290f), CHAR_REF("dblac;", 0x02dd), CHAR_REF("dcaron;", 0x010f), CHAR_REF("dcy;", 0x0434), CHAR_REF("dd;", 0x2146), CHAR_REF("ddagger;", 0x2021), CHAR_REF("ddarr;", 0x21ca), CHAR_REF("ddotseq;", 0x2a77), CHAR_REF("deg;", 0xb0), CHAR_REF("deg", 0xb0), CHAR_REF("delta;", 0x03b4), CHAR_REF("demptyv;", 0x29b1), CHAR_REF("dfisht;", 0x297f), CHAR_REF("dfr;", 0x0001d521), CHAR_REF("dharl;", 0x21c3), CHAR_REF("dharr;", 0x21c2), CHAR_REF("diam;", 0x22c4), CHAR_REF("diamond;", 0x22c4), CHAR_REF("diamondsuit;", 0x2666), CHAR_REF("diams;", 0x2666), CHAR_REF("die;", 0xa8), CHAR_REF("digamma;", 0x03dd), CHAR_REF("disin;", 0x22f2), CHAR_REF("div;", 0xf7), CHAR_REF("divide;", 0xf7), CHAR_REF("divide", 0xf7), CHAR_REF("divideontimes;", 0x22c7), CHAR_REF("divonx;", 0x22c7), CHAR_REF("djcy;", 0x0452), CHAR_REF("dlcorn;", 0x231e), CHAR_REF("dlcrop;", 0x230d), CHAR_REF("dollar;", 0x24), CHAR_REF("dopf;", 0x0001d555), CHAR_REF("dot;", 0x02d9), CHAR_REF("doteq;", 0x2250), CHAR_REF("doteqdot;", 0x2251), CHAR_REF("dotminus;", 0x2238), CHAR_REF("dotplus;", 0x2214), CHAR_REF("dotsquare;", 0x22a1), CHAR_REF("doublebarwedge;", 0x2306), CHAR_REF("downarrow;", 0x2193), CHAR_REF("downdownarrows;", 0x21ca), CHAR_REF("downharpoonleft;", 0x21c3), CHAR_REF("downharpoonright;", 0x21c2), CHAR_REF("drbkarow;", 0x2910), CHAR_REF("drcorn;", 0x231f), CHAR_REF("drcrop;", 0x230c), CHAR_REF("dscr;", 0x0001d4b9), CHAR_REF("dscy;", 0x0455), CHAR_REF("dsol;", 0x29f6), CHAR_REF("dstrok;", 0x0111), CHAR_REF("dtdot;", 0x22f1), CHAR_REF("dtri;", 0x25bf), CHAR_REF("dtrif;", 0x25be), CHAR_REF("duarr;", 0x21f5), CHAR_REF("duhar;", 0x296f), CHAR_REF("dwangle;", 0x29a6), CHAR_REF("dzcy;", 0x045f), CHAR_REF("dzigrarr;", 0x27ff), CHAR_REF("eDDot;", 0x2a77), CHAR_REF("eDot;", 0x2251), CHAR_REF("eacute;", 0xe9), CHAR_REF("eacute", 0xe9), CHAR_REF("easter;", 0x2a6e), CHAR_REF("ecaron;", 0x011b), CHAR_REF("ecir;", 0x2256), CHAR_REF("ecirc;", 0xea), CHAR_REF("ecirc", 0xea), CHAR_REF("ecolon;", 0x2255), CHAR_REF("ecy;", 0x044d), CHAR_REF("edot;", 0x0117), CHAR_REF("ee;", 0x2147), CHAR_REF("efDot;", 0x2252), CHAR_REF("efr;", 0x0001d522), CHAR_REF("eg;", 0x2a9a), CHAR_REF("egrave;", 0xe8), CHAR_REF("egrave", 0xe8), CHAR_REF("egs;", 0x2a96), CHAR_REF("egsdot;", 0x2a98), CHAR_REF("el;", 0x2a99), CHAR_REF("elinters;", 0x23e7), CHAR_REF("ell;", 0x2113), CHAR_REF("els;", 0x2a95), CHAR_REF("elsdot;", 0x2a97), CHAR_REF("emacr;", 0x0113), CHAR_REF("empty;", 0x2205), CHAR_REF("emptyset;", 0x2205), CHAR_REF("emptyv;", 0x2205), CHAR_REF("emsp13;", 0x2004), CHAR_REF("emsp14;", 0x2005), CHAR_REF("emsp;", 0x2003), CHAR_REF("eng;", 0x014b), CHAR_REF("ensp;", 0x2002), CHAR_REF("eogon;", 0x0119), CHAR_REF("eopf;", 0x0001d556), CHAR_REF("epar;", 0x22d5), CHAR_REF("eparsl;", 0x29e3), CHAR_REF("eplus;", 0x2a71), CHAR_REF("epsi;", 0x03b5), CHAR_REF("epsilon;", 0x03b5), CHAR_REF("epsiv;", 0x03f5), CHAR_REF("eqcirc;", 0x2256), CHAR_REF("eqcolon;", 0x2255), CHAR_REF("eqsim;", 0x2242), CHAR_REF("eqslantgtr;", 0x2a96), CHAR_REF("eqslantless;", 0x2a95), CHAR_REF("equals;", 0x3d), CHAR_REF("equest;", 0x225f), CHAR_REF("equiv;", 0x2261), CHAR_REF("equivDD;", 0x2a78), CHAR_REF("eqvparsl;", 0x29e5), CHAR_REF("erDot;", 0x2253), CHAR_REF("erarr;", 0x2971), CHAR_REF("escr;", 0x212f), CHAR_REF("esdot;", 0x2250), CHAR_REF("esim;", 0x2242), CHAR_REF("eta;", 0x03b7), CHAR_REF("eth;", 0xf0), CHAR_REF("eth", 0xf0), CHAR_REF("euml;", 0xeb), CHAR_REF("euml", 0xeb), CHAR_REF("euro;", 0x20ac), CHAR_REF("excl;", 0x21), CHAR_REF("exist;", 0x2203), CHAR_REF("expectation;", 0x2130), CHAR_REF("exponentiale;", 0x2147), CHAR_REF("fallingdotseq;", 0x2252), CHAR_REF("fcy;", 0x0444), CHAR_REF("female;", 0x2640), CHAR_REF("ffilig;", 0xfb03), CHAR_REF("fflig;", 0xfb00), CHAR_REF("ffllig;", 0xfb04), CHAR_REF("ffr;", 0x0001d523), CHAR_REF("filig;", 0xfb01), MULTI_CHAR_REF("fjlig;", 0x66, 0x6a), CHAR_REF("flat;", 0x266d), CHAR_REF("fllig;", 0xfb02), CHAR_REF("fltns;", 0x25b1), CHAR_REF("fnof;", 0x0192), CHAR_REF("fopf;", 0x0001d557), CHAR_REF("forall;", 0x2200), CHAR_REF("fork;", 0x22d4), CHAR_REF("forkv;", 0x2ad9), CHAR_REF("fpartint;", 0x2a0d), CHAR_REF("frac12", 0xbd), CHAR_REF("frac12;", 0xbd), CHAR_REF("frac13;", 0x2153), CHAR_REF("frac14", 0xbc), CHAR_REF("frac14;", 0xbc), CHAR_REF("frac15;", 0x2155), CHAR_REF("frac16;", 0x2159), CHAR_REF("frac18;", 0x215b), CHAR_REF("frac23;", 0x2154), CHAR_REF("frac25;", 0x2156), CHAR_REF("frac34", 0xbe), CHAR_REF("frac34;", 0xbe), CHAR_REF("frac35;", 0x2157), CHAR_REF("frac38;", 0x215c), CHAR_REF("frac45;", 0x2158), CHAR_REF("frac56;", 0x215a), CHAR_REF("frac58;", 0x215d), CHAR_REF("frac78;", 0x215e), CHAR_REF("frasl;", 0x2044), CHAR_REF("frown;", 0x2322), CHAR_REF("fscr;", 0x0001d4bb), CHAR_REF("gE;", 0x2267), CHAR_REF("gEl;", 0x2a8c), CHAR_REF("gacute;", 0x01f5), CHAR_REF("gamma;", 0x03b3), CHAR_REF("gammad;", 0x03dd), CHAR_REF("gap;", 0x2a86), CHAR_REF("gbreve;", 0x011f), CHAR_REF("gcirc;", 0x011d), CHAR_REF("gcy;", 0x0433), CHAR_REF("gdot;", 0x0121), CHAR_REF("ge;", 0x2265), CHAR_REF("gel;", 0x22db), CHAR_REF("geq;", 0x2265), CHAR_REF("geqq;", 0x2267), CHAR_REF("geqslant;", 0x2a7e), CHAR_REF("ges;", 0x2a7e), CHAR_REF("gescc;", 0x2aa9), CHAR_REF("gesdot;", 0x2a80), CHAR_REF("gesdoto;", 0x2a82), CHAR_REF("gesdotol;", 0x2a84), MULTI_CHAR_REF("gesl;", 0x22db, 0xfe00), CHAR_REF("gesles;", 0x2a94), CHAR_REF("gfr;", 0x0001d524), CHAR_REF("gg;", 0x226b), CHAR_REF("ggg;", 0x22d9), CHAR_REF("gimel;", 0x2137), CHAR_REF("gjcy;", 0x0453), CHAR_REF("gl;", 0x2277), CHAR_REF("glE;", 0x2a92), CHAR_REF("gla;", 0x2aa5), CHAR_REF("glj;", 0x2aa4), CHAR_REF("gnE;", 0x2269), CHAR_REF("gnap;", 0x2a8a), CHAR_REF("gnapprox;", 0x2a8a), CHAR_REF("gne;", 0x2a88), CHAR_REF("gneq;", 0x2a88), CHAR_REF("gneqq;", 0x2269), CHAR_REF("gnsim;", 0x22e7), CHAR_REF("gopf;", 0x0001d558), CHAR_REF("grave;", 0x60), CHAR_REF("gscr;", 0x210a), CHAR_REF("gsim;", 0x2273), CHAR_REF("gsime;", 0x2a8e), CHAR_REF("gsiml;", 0x2a90), CHAR_REF("gt;", 0x3e), CHAR_REF("gt", 0x3e), CHAR_REF("gtcc;", 0x2aa7), CHAR_REF("gtcir;", 0x2a7a), CHAR_REF("gtdot;", 0x22d7), CHAR_REF("gtlPar;", 0x2995), CHAR_REF("gtquest;", 0x2a7c), CHAR_REF("gtrapprox;", 0x2a86), CHAR_REF("gtrarr;", 0x2978), CHAR_REF("gtrdot;", 0x22d7), CHAR_REF("gtreqless;", 0x22db), CHAR_REF("gtreqqless;", 0x2a8c), CHAR_REF("gtrless;", 0x2277), CHAR_REF("gtrsim;", 0x2273), MULTI_CHAR_REF("gvertneqq;", 0x2269, 0xfe00), MULTI_CHAR_REF("gvnE;", 0x2269, 0xfe00), CHAR_REF("hArr;", 0x21d4), CHAR_REF("hairsp;", 0x200a), CHAR_REF("half;", 0xbd), CHAR_REF("hamilt;", 0x210b), CHAR_REF("hardcy;", 0x044a), CHAR_REF("harr;", 0x2194), CHAR_REF("harrcir;", 0x2948), CHAR_REF("harrw;", 0x21ad), CHAR_REF("hbar;", 0x210f), CHAR_REF("hcirc;", 0x0125), CHAR_REF("hearts;", 0x2665), CHAR_REF("heartsuit;", 0x2665), CHAR_REF("hellip;", 0x2026), CHAR_REF("hercon;", 0x22b9), CHAR_REF("hfr;", 0x0001d525), CHAR_REF("hksearow;", 0x2925), CHAR_REF("hkswarow;", 0x2926), CHAR_REF("hoarr;", 0x21ff), CHAR_REF("homtht;", 0x223b), CHAR_REF("hookleftarrow;", 0x21a9), CHAR_REF("hookrightarrow;", 0x21aa), CHAR_REF("hopf;", 0x0001d559), CHAR_REF("horbar;", 0x2015), CHAR_REF("hscr;", 0x0001d4bd), CHAR_REF("hslash;", 0x210f), CHAR_REF("hstrok;", 0x0127), CHAR_REF("hybull;", 0x2043), CHAR_REF("hyphen;", 0x2010), CHAR_REF("iacute;", 0xed), CHAR_REF("iacute", 0xed), CHAR_REF("ic;", 0x2063), CHAR_REF("icirc;", 0xee), CHAR_REF("icirc", 0xee), CHAR_REF("icy;", 0x0438), CHAR_REF("iecy;", 0x0435), CHAR_REF("iexcl;", 0xa1), CHAR_REF("iexcl", 0xa1), CHAR_REF("iff;", 0x21d4), CHAR_REF("ifr;", 0x0001d526), CHAR_REF("igrave;", 0xec), CHAR_REF("igrave", 0xec), CHAR_REF("ii;", 0x2148), CHAR_REF("iiiint;", 0x2a0c), CHAR_REF("iiint;", 0x222d), CHAR_REF("iinfin;", 0x29dc), CHAR_REF("iiota;", 0x2129), CHAR_REF("ijlig;", 0x0133), CHAR_REF("imacr;", 0x012b), CHAR_REF("image;", 0x2111), CHAR_REF("imagline;", 0x2110), CHAR_REF("imagpart;", 0x2111), CHAR_REF("imath;", 0x0131), CHAR_REF("imof;", 0x22b7), CHAR_REF("imped;", 0x01b5), CHAR_REF("in;", 0x2208), CHAR_REF("incare;", 0x2105), CHAR_REF("infin;", 0x221e), CHAR_REF("infintie;", 0x29dd), CHAR_REF("inodot;", 0x0131), CHAR_REF("int;", 0x222b), CHAR_REF("intcal;", 0x22ba), CHAR_REF("integers;", 0x2124), CHAR_REF("intercal;", 0x22ba), CHAR_REF("intlarhk;", 0x2a17), CHAR_REF("intprod;", 0x2a3c), CHAR_REF("iocy;", 0x0451), CHAR_REF("iogon;", 0x012f), CHAR_REF("iopf;", 0x0001d55a), CHAR_REF("iota;", 0x03b9), CHAR_REF("iprod;", 0x2a3c), CHAR_REF("iquest;", 0xbf), CHAR_REF("iquest", 0xbf), CHAR_REF("iscr;", 0x0001d4be), CHAR_REF("isin;", 0x2208), CHAR_REF("isinE;", 0x22f9), CHAR_REF("isindot;", 0x22f5), CHAR_REF("isins;", 0x22f4), CHAR_REF("isinsv;", 0x22f3), CHAR_REF("isinv;", 0x2208), CHAR_REF("it;", 0x2062), CHAR_REF("itilde;", 0x0129), CHAR_REF("iukcy;", 0x0456), CHAR_REF("iuml;", 0xef), CHAR_REF("iuml", 0xef), CHAR_REF("jcirc;", 0x0135), CHAR_REF("jcy;", 0x0439), CHAR_REF("jfr;", 0x0001d527), CHAR_REF("jmath;", 0x0237), CHAR_REF("jopf;", 0x0001d55b), CHAR_REF("jscr;", 0x0001d4bf), CHAR_REF("jsercy;", 0x0458), CHAR_REF("jukcy;", 0x0454), CHAR_REF("kappa;", 0x03ba), CHAR_REF("kappav;", 0x03f0), CHAR_REF("kcedil;", 0x0137), CHAR_REF("kcy;", 0x043a), CHAR_REF("kfr;", 0x0001d528), CHAR_REF("kgreen;", 0x0138), CHAR_REF("khcy;", 0x0445), CHAR_REF("kjcy;", 0x045c), CHAR_REF("kopf;", 0x0001d55c), CHAR_REF("kscr;", 0x0001d4c0), CHAR_REF("lAarr;", 0x21da), CHAR_REF("lArr;", 0x21d0), CHAR_REF("lAtail;", 0x291b), CHAR_REF("lBarr;", 0x290e), CHAR_REF("lE;", 0x2266), CHAR_REF("lEg;", 0x2a8b), CHAR_REF("lHar;", 0x2962), CHAR_REF("lacute;", 0x013a), CHAR_REF("laemptyv;", 0x29b4), CHAR_REF("lagran;", 0x2112), CHAR_REF("lambda;", 0x03bb), CHAR_REF("lang;", 0x27e8), CHAR_REF("langd;", 0x2991), CHAR_REF("langle;", 0x27e8), CHAR_REF("lap;", 0x2a85), CHAR_REF("laquo;", 0xab), CHAR_REF("laquo", 0xab), CHAR_REF("larr;", 0x2190), CHAR_REF("larrb;", 0x21e4), CHAR_REF("larrbfs;", 0x291f), CHAR_REF("larrfs;", 0x291d), CHAR_REF("larrhk;", 0x21a9), CHAR_REF("larrlp;", 0x21ab), CHAR_REF("larrpl;", 0x2939), CHAR_REF("larrsim;", 0x2973), CHAR_REF("larrtl;", 0x21a2), CHAR_REF("lat;", 0x2aab), CHAR_REF("latail;", 0x2919), CHAR_REF("late;", 0x2aad), MULTI_CHAR_REF("lates;", 0x2aad, 0xfe00), CHAR_REF("lbarr;", 0x290c), CHAR_REF("lbbrk;", 0x2772), CHAR_REF("lbrace;", 0x7b), CHAR_REF("lbrack;", 0x5b), CHAR_REF("lbrke;", 0x298b), CHAR_REF("lbrksld;", 0x298f), CHAR_REF("lbrkslu;", 0x298d), CHAR_REF("lcaron;", 0x013e), CHAR_REF("lcedil;", 0x013c), CHAR_REF("lceil;", 0x2308), CHAR_REF("lcub;", 0x7b), CHAR_REF("lcy;", 0x043b), CHAR_REF("ldca;", 0x2936), CHAR_REF("ldquo;", 0x201c), CHAR_REF("ldquor;", 0x201e), CHAR_REF("ldrdhar;", 0x2967), CHAR_REF("ldrushar;", 0x294b), CHAR_REF("ldsh;", 0x21b2), CHAR_REF("le;", 0x2264), CHAR_REF("leftarrow;", 0x2190), CHAR_REF("leftarrowtail;", 0x21a2), CHAR_REF("leftharpoondown;", 0x21bd), CHAR_REF("leftharpoonup;", 0x21bc), CHAR_REF("leftleftarrows;", 0x21c7), CHAR_REF("leftrightarrow;", 0x2194), CHAR_REF("leftrightarrows;", 0x21c6), CHAR_REF("leftrightharpoons;", 0x21cb), CHAR_REF("leftrightsquigarrow;", 0x21ad), CHAR_REF("leftthreetimes;", 0x22cb), CHAR_REF("leg;", 0x22da), CHAR_REF("leq;", 0x2264), CHAR_REF("leqq;", 0x2266), CHAR_REF("leqslant;", 0x2a7d), CHAR_REF("les;", 0x2a7d), CHAR_REF("lescc;", 0x2aa8), CHAR_REF("lesdot;", 0x2a7f), CHAR_REF("lesdoto;", 0x2a81), CHAR_REF("lesdotor;", 0x2a83), MULTI_CHAR_REF("lesg;", 0x22da, 0xfe00), CHAR_REF("lesges;", 0x2a93), CHAR_REF("lessapprox;", 0x2a85), CHAR_REF("lessdot;", 0x22d6), CHAR_REF("lesseqgtr;", 0x22da), CHAR_REF("lesseqqgtr;", 0x2a8b), CHAR_REF("lessgtr;", 0x2276), CHAR_REF("lesssim;", 0x2272), CHAR_REF("lfisht;", 0x297c), CHAR_REF("lfloor;", 0x230a), CHAR_REF("lfr;", 0x0001d529), CHAR_REF("lg;", 0x2276), CHAR_REF("lgE;", 0x2a91), CHAR_REF("lhard;", 0x21bd), CHAR_REF("lharu;", 0x21bc), CHAR_REF("lharul;", 0x296a), CHAR_REF("lhblk;", 0x2584), CHAR_REF("ljcy;", 0x0459), CHAR_REF("ll;", 0x226a), CHAR_REF("llarr;", 0x21c7), CHAR_REF("llcorner;", 0x231e), CHAR_REF("llhard;", 0x296b), CHAR_REF("lltri;", 0x25fa), CHAR_REF("lmidot;", 0x0140), CHAR_REF("lmoust;", 0x23b0), CHAR_REF("lmoustache;", 0x23b0), CHAR_REF("lnE;", 0x2268), CHAR_REF("lnap;", 0x2a89), CHAR_REF("lnapprox;", 0x2a89), CHAR_REF("lne;", 0x2a87), CHAR_REF("lneq;", 0x2a87), CHAR_REF("lneqq;", 0x2268), CHAR_REF("lnsim;", 0x22e6), CHAR_REF("loang;", 0x27ec), CHAR_REF("loarr;", 0x21fd), CHAR_REF("lobrk;", 0x27e6), CHAR_REF("longleftarrow;", 0x27f5), CHAR_REF("longleftrightarrow;", 0x27f7), CHAR_REF("longmapsto;", 0x27fc), CHAR_REF("longrightarrow;", 0x27f6), CHAR_REF("looparrowleft;", 0x21ab), CHAR_REF("looparrowright;", 0x21ac), CHAR_REF("lopar;", 0x2985), CHAR_REF("lopf;", 0x0001d55d), CHAR_REF("loplus;", 0x2a2d), CHAR_REF("lotimes;", 0x2a34), CHAR_REF("lowast;", 0x2217), CHAR_REF("lowbar;", 0x5f), CHAR_REF("loz;", 0x25ca), CHAR_REF("lozenge;", 0x25ca), CHAR_REF("lozf;", 0x29eb), CHAR_REF("lpar;", 0x28), CHAR_REF("lparlt;", 0x2993), CHAR_REF("lrarr;", 0x21c6), CHAR_REF("lrcorner;", 0x231f), CHAR_REF("lrhar;", 0x21cb), CHAR_REF("lrhard;", 0x296d), CHAR_REF("lrm;", 0x200e), CHAR_REF("lrtri;", 0x22bf), CHAR_REF("lsaquo;", 0x2039), CHAR_REF("lscr;", 0x0001d4c1), CHAR_REF("lsh;", 0x21b0), CHAR_REF("lsim;", 0x2272), CHAR_REF("lsime;", 0x2a8d), CHAR_REF("lsimg;", 0x2a8f), CHAR_REF("lsqb;", 0x5b), CHAR_REF("lsquo;", 0x2018), CHAR_REF("lsquor;", 0x201a), CHAR_REF("lstrok;", 0x0142), CHAR_REF("lt;", 0x3c), CHAR_REF("lt", 0x3c), CHAR_REF("ltcc;", 0x2aa6), CHAR_REF("ltcir;", 0x2a79), CHAR_REF("ltdot;", 0x22d6), CHAR_REF("lthree;", 0x22cb), CHAR_REF("ltimes;", 0x22c9), CHAR_REF("ltlarr;", 0x2976), CHAR_REF("ltquest;", 0x2a7b), CHAR_REF("ltrPar;", 0x2996), CHAR_REF("ltri;", 0x25c3), CHAR_REF("ltrie;", 0x22b4), CHAR_REF("ltrif;", 0x25c2), CHAR_REF("lurdshar;", 0x294a), CHAR_REF("luruhar;", 0x2966), MULTI_CHAR_REF("lvertneqq;", 0x2268, 0xfe00), MULTI_CHAR_REF("lvnE;", 0x2268, 0xfe00), CHAR_REF("mDDot;", 0x223a), CHAR_REF("macr;", 0xaf), CHAR_REF("macr", 0xaf), CHAR_REF("male;", 0x2642), CHAR_REF("malt;", 0x2720), CHAR_REF("maltese;", 0x2720), CHAR_REF("map;", 0x21a6), CHAR_REF("mapsto;", 0x21a6), CHAR_REF("mapstodown;", 0x21a7), CHAR_REF("mapstoleft;", 0x21a4), CHAR_REF("mapstoup;", 0x21a5), CHAR_REF("marker;", 0x25ae), CHAR_REF("mcomma;", 0x2a29), CHAR_REF("mcy;", 0x043c), CHAR_REF("mdash;", 0x2014), CHAR_REF("measuredangle;", 0x2221), CHAR_REF("mfr;", 0x0001d52a), CHAR_REF("mho;", 0x2127), CHAR_REF("micro;", 0xb5), CHAR_REF("micro", 0xb5), CHAR_REF("mid;", 0x2223), CHAR_REF("midast;", 0x2a), CHAR_REF("midcir;", 0x2af0), CHAR_REF("middot;", 0xb7), CHAR_REF("middot", 0xb7), CHAR_REF("minus;", 0x2212), CHAR_REF("minusb;", 0x229f), CHAR_REF("minusd;", 0x2238), CHAR_REF("minusdu;", 0x2a2a), CHAR_REF("mlcp;", 0x2adb), CHAR_REF("mldr;", 0x2026), CHAR_REF("mnplus;", 0x2213), CHAR_REF("models;", 0x22a7), CHAR_REF("mopf;", 0x0001d55e), CHAR_REF("mp;", 0x2213), CHAR_REF("mscr;", 0x0001d4c2), CHAR_REF("mstpos;", 0x223e), CHAR_REF("mu;", 0x03bc), CHAR_REF("multimap;", 0x22b8), CHAR_REF("mumap;", 0x22b8), MULTI_CHAR_REF("nGg;", 0x22d9, 0x0338), MULTI_CHAR_REF("nGt;", 0x226b, 0x20d2), MULTI_CHAR_REF("nGtv;", 0x226b, 0x0338), CHAR_REF("nLeftarrow;", 0x21cd), CHAR_REF("nLeftrightarrow;", 0x21ce), MULTI_CHAR_REF("nLl;", 0x22d8, 0x0338), MULTI_CHAR_REF("nLt;", 0x226a, 0x20d2), MULTI_CHAR_REF("nLtv;", 0x226a, 0x0338), CHAR_REF("nRightarrow;", 0x21cf), CHAR_REF("nVDash;", 0x22af), CHAR_REF("nVdash;", 0x22ae), CHAR_REF("nabla;", 0x2207), CHAR_REF("nacute;", 0x0144), MULTI_CHAR_REF("nang;", 0x2220, 0x20d2), CHAR_REF("nap;", 0x2249), MULTI_CHAR_REF("napE;", 0x2a70, 0x0338), MULTI_CHAR_REF("napid;", 0x224b, 0x0338), CHAR_REF("napos;", 0x0149), CHAR_REF("napprox;", 0x2249), CHAR_REF("natur;", 0x266e), CHAR_REF("natural;", 0x266e), CHAR_REF("naturals;", 0x2115), CHAR_REF("nbsp;", 0xa0), CHAR_REF("nbsp", 0xa0), MULTI_CHAR_REF("nbump;", 0x224e, 0x0338), MULTI_CHAR_REF("nbumpe;", 0x224f, 0x0338), CHAR_REF("ncap;", 0x2a43), CHAR_REF("ncaron;", 0x0148), CHAR_REF("ncedil;", 0x0146), CHAR_REF("ncong;", 0x2247), MULTI_CHAR_REF("ncongdot;", 0x2a6d, 0x0338), CHAR_REF("ncup;", 0x2a42), CHAR_REF("ncy;", 0x043d), CHAR_REF("ndash;", 0x2013), CHAR_REF("ne;", 0x2260), CHAR_REF("neArr;", 0x21d7), CHAR_REF("nearhk;", 0x2924), CHAR_REF("nearr;", 0x2197), CHAR_REF("nearrow;", 0x2197), MULTI_CHAR_REF("nedot;", 0x2250, 0x0338), CHAR_REF("nequiv;", 0x2262), CHAR_REF("nesear;", 0x2928), MULTI_CHAR_REF("nesim;", 0x2242, 0x0338), CHAR_REF("nexist;", 0x2204), CHAR_REF("nexists;", 0x2204), CHAR_REF("nfr;", 0x0001d52b), MULTI_CHAR_REF("ngE;", 0x2267, 0x0338), CHAR_REF("nge;", 0x2271), CHAR_REF("ngeq;", 0x2271), MULTI_CHAR_REF("ngeqq;", 0x2267, 0x0338), MULTI_CHAR_REF("ngeqslant;", 0x2a7e, 0x0338), MULTI_CHAR_REF("nges;", 0x2a7e, 0x0338), CHAR_REF("ngsim;", 0x2275), CHAR_REF("ngt;", 0x226f), CHAR_REF("ngtr;", 0x226f), CHAR_REF("nhArr;", 0x21ce), CHAR_REF("nharr;", 0x21ae), CHAR_REF("nhpar;", 0x2af2), CHAR_REF("ni;", 0x220b), CHAR_REF("nis;", 0x22fc), CHAR_REF("nisd;", 0x22fa), CHAR_REF("niv;", 0x220b), CHAR_REF("njcy;", 0x045a), CHAR_REF("nlArr;", 0x21cd), MULTI_CHAR_REF("nlE;", 0x2266, 0x0338), CHAR_REF("nlarr;", 0x219a), CHAR_REF("nldr;", 0x2025), CHAR_REF("nle;", 0x2270), CHAR_REF("nleftarrow;", 0x219a), CHAR_REF("nleftrightarrow;", 0x21ae), CHAR_REF("nleq;", 0x2270), MULTI_CHAR_REF("nleqq;", 0x2266, 0x0338), MULTI_CHAR_REF("nleqslant;", 0x2a7d, 0x0338), MULTI_CHAR_REF("nles;", 0x2a7d, 0x0338), CHAR_REF("nless;", 0x226e), CHAR_REF("nlsim;", 0x2274), CHAR_REF("nlt;", 0x226e), CHAR_REF("nltri;", 0x22ea), CHAR_REF("nltrie;", 0x22ec), CHAR_REF("nmid;", 0x2224), CHAR_REF("nopf;", 0x0001d55f), CHAR_REF("not;", 0xac), CHAR_REF("notin;", 0x2209), MULTI_CHAR_REF("notinE;", 0x22f9, 0x0338), MULTI_CHAR_REF("notindot;", 0x22f5, 0x0338), CHAR_REF("notinva;", 0x2209), CHAR_REF("notinvb;", 0x22f7), CHAR_REF("notinvc;", 0x22f6), CHAR_REF("notni;", 0x220c), CHAR_REF("notniva;", 0x220c), CHAR_REF("notnivb;", 0x22fe), CHAR_REF("notnivc;", 0x22fd), CHAR_REF("not", 0xac), CHAR_REF("npar;", 0x2226), CHAR_REF("nparallel;", 0x2226), MULTI_CHAR_REF("nparsl;", 0x2afd, 0x20e5), MULTI_CHAR_REF("npart;", 0x2202, 0x0338), CHAR_REF("npolint;", 0x2a14), CHAR_REF("npr;", 0x2280), CHAR_REF("nprcue;", 0x22e0), MULTI_CHAR_REF("npre;", 0x2aaf, 0x0338), CHAR_REF("nprec;", 0x2280), MULTI_CHAR_REF("npreceq;", 0x2aaf, 0x0338), CHAR_REF("nrArr;", 0x21cf), CHAR_REF("nrarr;", 0x219b), MULTI_CHAR_REF("nrarrc;", 0x2933, 0x0338), MULTI_CHAR_REF("nrarrw;", 0x219d, 0x0338), CHAR_REF("nrightarrow;", 0x219b), CHAR_REF("nrtri;", 0x22eb), CHAR_REF("nrtrie;", 0x22ed), CHAR_REF("nsc;", 0x2281), CHAR_REF("nsccue;", 0x22e1), MULTI_CHAR_REF("nsce;", 0x2ab0, 0x0338), CHAR_REF("nscr;", 0x0001d4c3), CHAR_REF("nshortmid;", 0x2224), CHAR_REF("nshortparallel;", 0x2226), CHAR_REF("nsim;", 0x2241), CHAR_REF("nsime;", 0x2244), CHAR_REF("nsimeq;", 0x2244), CHAR_REF("nsmid;", 0x2224), CHAR_REF("nspar;", 0x2226), CHAR_REF("nsqsube;", 0x22e2), CHAR_REF("nsqsupe;", 0x22e3), CHAR_REF("nsub;", 0x2284), MULTI_CHAR_REF("nsubE;", 0x2ac5, 0x0338), CHAR_REF("nsube;", 0x2288), MULTI_CHAR_REF("nsubset;", 0x2282, 0x20d2), CHAR_REF("nsubseteq;", 0x2288), MULTI_CHAR_REF("nsubseteqq;", 0x2ac5, 0x0338), CHAR_REF("nsucc;", 0x2281), MULTI_CHAR_REF("nsucceq;", 0x2ab0, 0x0338), CHAR_REF("nsup;", 0x2285), MULTI_CHAR_REF("nsupE;", 0x2ac6, 0x0338), CHAR_REF("nsupe;", 0x2289), MULTI_CHAR_REF("nsupset;", 0x2283, 0x20d2), CHAR_REF("nsupseteq;", 0x2289), MULTI_CHAR_REF("nsupseteqq;", 0x2ac6, 0x0338), CHAR_REF("ntgl;", 0x2279), CHAR_REF("ntilde;", 0xf1), CHAR_REF("ntilde", 0xf1), CHAR_REF("ntlg;", 0x2278), CHAR_REF("ntriangleleft;", 0x22ea), CHAR_REF("ntrianglelefteq;", 0x22ec), CHAR_REF("ntriangleright;", 0x22eb), CHAR_REF("ntrianglerighteq;", 0x22ed), CHAR_REF("nu;", 0x03bd), CHAR_REF("num;", 0x23), CHAR_REF("numero;", 0x2116), CHAR_REF("numsp;", 0x2007), CHAR_REF("nvDash;", 0x22ad), CHAR_REF("nvHarr;", 0x2904), MULTI_CHAR_REF("nvap;", 0x224d, 0x20d2), CHAR_REF("nvdash;", 0x22ac), MULTI_CHAR_REF("nvge;", 0x2265, 0x20d2), MULTI_CHAR_REF("nvgt;", 0x3e, 0x20d2), CHAR_REF("nvinfin;", 0x29de), CHAR_REF("nvlArr;", 0x2902), MULTI_CHAR_REF("nvle;", 0x2264, 0x20d2), MULTI_CHAR_REF("nvlt;", 0x3c, 0x20d2), MULTI_CHAR_REF("nvltrie;", 0x22b4, 0x20d2), CHAR_REF("nvrArr;", 0x2903), MULTI_CHAR_REF("nvrtrie;", 0x22b5, 0x20d2), MULTI_CHAR_REF("nvsim;", 0x223c, 0x20d2), CHAR_REF("nwArr;", 0x21d6), CHAR_REF("nwarhk;", 0x2923), CHAR_REF("nwarr;", 0x2196), CHAR_REF("nwarrow;", 0x2196), CHAR_REF("nwnear;", 0x2927), CHAR_REF("oS;", 0x24c8), CHAR_REF("oacute;", 0xf3), CHAR_REF("oacute", 0xf3), CHAR_REF("oast;", 0x229b), CHAR_REF("ocir;", 0x229a), CHAR_REF("ocirc;", 0xf4), CHAR_REF("ocirc", 0xf4), CHAR_REF("ocy;", 0x043e), CHAR_REF("odash;", 0x229d), CHAR_REF("odblac;", 0x0151), CHAR_REF("odiv;", 0x2a38), CHAR_REF("odot;", 0x2299), CHAR_REF("odsold;", 0x29bc), CHAR_REF("oelig;", 0x0153), CHAR_REF("ofcir;", 0x29bf), CHAR_REF("ofr;", 0x0001d52c), CHAR_REF("ogon;", 0x02db), CHAR_REF("ograve;", 0xf2), CHAR_REF("ograve", 0xf2), CHAR_REF("ogt;", 0x29c1), CHAR_REF("ohbar;", 0x29b5), CHAR_REF("ohm;", 0x03a9), CHAR_REF("oint;", 0x222e), CHAR_REF("olarr;", 0x21ba), CHAR_REF("olcir;", 0x29be), CHAR_REF("olcross;", 0x29bb), CHAR_REF("oline;", 0x203e), CHAR_REF("olt;", 0x29c0), CHAR_REF("omacr;", 0x014d), CHAR_REF("omega;", 0x03c9), CHAR_REF("omicron;", 0x03bf), CHAR_REF("omid;", 0x29b6), CHAR_REF("ominus;", 0x2296), CHAR_REF("oopf;", 0x0001d560), CHAR_REF("opar;", 0x29b7), CHAR_REF("operp;", 0x29b9), CHAR_REF("oplus;", 0x2295), CHAR_REF("or;", 0x2228), CHAR_REF("orarr;", 0x21bb), CHAR_REF("ord;", 0x2a5d), CHAR_REF("order;", 0x2134), CHAR_REF("orderof;", 0x2134), CHAR_REF("ordf;", 0xaa), CHAR_REF("ordf", 0xaa), CHAR_REF("ordm;", 0xba), CHAR_REF("ordm", 0xba), CHAR_REF("origof;", 0x22b6), CHAR_REF("oror;", 0x2a56), CHAR_REF("orslope;", 0x2a57), CHAR_REF("orv;", 0x2a5b), CHAR_REF("oscr;", 0x2134), CHAR_REF("oslash;", 0xf8), CHAR_REF("oslash", 0xf8), CHAR_REF("osol;", 0x2298), CHAR_REF("otilde;", 0xf5), CHAR_REF("otilde", 0xf5), CHAR_REF("otimes;", 0x2297), CHAR_REF("otimesas;", 0x2a36), CHAR_REF("ouml;", 0xf6), CHAR_REF("ouml", 0xf6), CHAR_REF("ovbar;", 0x233d), CHAR_REF("par;", 0x2225), CHAR_REF("para;", 0xb6), CHAR_REF("para", 0xb6), CHAR_REF("parallel;", 0x2225), CHAR_REF("parsim;", 0x2af3), CHAR_REF("parsl;", 0x2afd), CHAR_REF("part;", 0x2202), CHAR_REF("pcy;", 0x043f), CHAR_REF("percnt;", 0x25), CHAR_REF("period;", 0x2e), CHAR_REF("permil;", 0x2030), CHAR_REF("perp;", 0x22a5), CHAR_REF("pertenk;", 0x2031), CHAR_REF("pfr;", 0x0001d52d), CHAR_REF("phi;", 0x03c6), CHAR_REF("phiv;", 0x03d5), CHAR_REF("phmmat;", 0x2133), CHAR_REF("phone;", 0x260e), CHAR_REF("pi;", 0x03c0), CHAR_REF("pitchfork;", 0x22d4), CHAR_REF("piv;", 0x03d6), CHAR_REF("planck;", 0x210f), CHAR_REF("planckh;", 0x210e), CHAR_REF("plankv;", 0x210f), CHAR_REF("plus;", 0x2b), CHAR_REF("plusacir;", 0x2a23), CHAR_REF("plusb;", 0x229e), CHAR_REF("pluscir;", 0x2a22), CHAR_REF("plusdo;", 0x2214), CHAR_REF("plusdu;", 0x2a25), CHAR_REF("pluse;", 0x2a72), CHAR_REF("plusmn;", 0xb1), CHAR_REF("plusmn", 0xb1), CHAR_REF("plussim;", 0x2a26), CHAR_REF("plustwo;", 0x2a27), CHAR_REF("pm;", 0xb1), CHAR_REF("pointint;", 0x2a15), CHAR_REF("popf;", 0x0001d561), CHAR_REF("pound;", 0xa3), CHAR_REF("pound", 0xa3), CHAR_REF("pr;", 0x227a), CHAR_REF("prE;", 0x2ab3), CHAR_REF("prap;", 0x2ab7), CHAR_REF("prcue;", 0x227c), CHAR_REF("pre;", 0x2aaf), CHAR_REF("prec;", 0x227a), CHAR_REF("precapprox;", 0x2ab7), CHAR_REF("preccurlyeq;", 0x227c), CHAR_REF("preceq;", 0x2aaf), CHAR_REF("precnapprox;", 0x2ab9), CHAR_REF("precneqq;", 0x2ab5), CHAR_REF("precnsim;", 0x22e8), CHAR_REF("precsim;", 0x227e), CHAR_REF("prime;", 0x2032), CHAR_REF("primes;", 0x2119), CHAR_REF("prnE;", 0x2ab5), CHAR_REF("prnap;", 0x2ab9), CHAR_REF("prnsim;", 0x22e8), CHAR_REF("prod;", 0x220f), CHAR_REF("profalar;", 0x232e), CHAR_REF("profline;", 0x2312), CHAR_REF("profsurf;", 0x2313), CHAR_REF("prop;", 0x221d), CHAR_REF("propto;", 0x221d), CHAR_REF("prsim;", 0x227e), CHAR_REF("prurel;", 0x22b0), CHAR_REF("pscr;", 0x0001d4c5), CHAR_REF("psi;", 0x03c8), CHAR_REF("puncsp;", 0x2008), CHAR_REF("qfr;", 0x0001d52e), CHAR_REF("qint;", 0x2a0c), CHAR_REF("qopf;", 0x0001d562), CHAR_REF("qprime;", 0x2057), CHAR_REF("qscr;", 0x0001d4c6), CHAR_REF("quaternions;", 0x210d), CHAR_REF("quatint;", 0x2a16), CHAR_REF("quest;", 0x3f), CHAR_REF("questeq;", 0x225f), CHAR_REF("quot;", 0x22), CHAR_REF("quot", 0x22), CHAR_REF("rAarr;", 0x21db), CHAR_REF("rArr;", 0x21d2), CHAR_REF("rAtail;", 0x291c), CHAR_REF("rBarr;", 0x290f), CHAR_REF("rHar;", 0x2964), MULTI_CHAR_REF("race;", 0x223d, 0x0331), CHAR_REF("racute;", 0x0155), CHAR_REF("radic;", 0x221a), CHAR_REF("raemptyv;", 0x29b3), CHAR_REF("rang;", 0x27e9), CHAR_REF("rangd;", 0x2992), CHAR_REF("range;", 0x29a5), CHAR_REF("rangle;", 0x27e9), CHAR_REF("raquo;", 0xbb), CHAR_REF("raquo", 0xbb), CHAR_REF("rarr;", 0x2192), CHAR_REF("rarrap;", 0x2975), CHAR_REF("rarrb;", 0x21e5), CHAR_REF("rarrbfs;", 0x2920), CHAR_REF("rarrc;", 0x2933), CHAR_REF("rarrfs;", 0x291e), CHAR_REF("rarrhk;", 0x21aa), CHAR_REF("rarrlp;", 0x21ac), CHAR_REF("rarrpl;", 0x2945), CHAR_REF("rarrsim;", 0x2974), CHAR_REF("rarrtl;", 0x21a3), CHAR_REF("rarrw;", 0x219d), CHAR_REF("ratail;", 0x291a), CHAR_REF("ratio;", 0x2236), CHAR_REF("rationals;", 0x211a), CHAR_REF("rbarr;", 0x290d), CHAR_REF("rbbrk;", 0x2773), CHAR_REF("rbrace;", 0x7d), CHAR_REF("rbrack;", 0x5d), CHAR_REF("rbrke;", 0x298c), CHAR_REF("rbrksld;", 0x298e), CHAR_REF("rbrkslu;", 0x2990), CHAR_REF("rcaron;", 0x0159), CHAR_REF("rcedil;", 0x0157), CHAR_REF("rceil;", 0x2309), CHAR_REF("rcub;", 0x7d), CHAR_REF("rcy;", 0x0440), CHAR_REF("rdca;", 0x2937), CHAR_REF("rdldhar;", 0x2969), CHAR_REF("rdquo;", 0x201d), CHAR_REF("rdquor;", 0x201d), CHAR_REF("rdsh;", 0x21b3), CHAR_REF("real;", 0x211c), CHAR_REF("realine;", 0x211b), CHAR_REF("realpart;", 0x211c), CHAR_REF("reals;", 0x211d), CHAR_REF("rect;", 0x25ad), CHAR_REF("reg;", 0xae), CHAR_REF("reg", 0xae), CHAR_REF("rfisht;", 0x297d), CHAR_REF("rfloor;", 0x230b), CHAR_REF("rfr;", 0x0001d52f), CHAR_REF("rhard;", 0x21c1), CHAR_REF("rharu;", 0x21c0), CHAR_REF("rharul;", 0x296c), CHAR_REF("rho;", 0x03c1), CHAR_REF("rhov;", 0x03f1), CHAR_REF("rightarrow;", 0x2192), CHAR_REF("rightarrowtail;", 0x21a3), CHAR_REF("rightharpoondown;", 0x21c1), CHAR_REF("rightharpoonup;", 0x21c0), CHAR_REF("rightleftarrows;", 0x21c4), CHAR_REF("rightleftharpoons;", 0x21cc), CHAR_REF("rightrightarrows;", 0x21c9), CHAR_REF("rightsquigarrow;", 0x219d), CHAR_REF("rightthreetimes;", 0x22cc), CHAR_REF("ring;", 0x02da), CHAR_REF("risingdotseq;", 0x2253), CHAR_REF("rlarr;", 0x21c4), CHAR_REF("rlhar;", 0x21cc), CHAR_REF("rlm;", 0x200f), CHAR_REF("rmoust;", 0x23b1), CHAR_REF("rmoustache;", 0x23b1), CHAR_REF("rnmid;", 0x2aee), CHAR_REF("roang;", 0x27ed), CHAR_REF("roarr;", 0x21fe), CHAR_REF("robrk;", 0x27e7), CHAR_REF("ropar;", 0x2986), CHAR_REF("ropf;", 0x0001d563), CHAR_REF("roplus;", 0x2a2e), CHAR_REF("rotimes;", 0x2a35), CHAR_REF("rpar;", 0x29), CHAR_REF("rpargt;", 0x2994), CHAR_REF("rppolint;", 0x2a12), CHAR_REF("rrarr;", 0x21c9), CHAR_REF("rsaquo;", 0x203a), CHAR_REF("rscr;", 0x0001d4c7), CHAR_REF("rsh;", 0x21b1), CHAR_REF("rsqb;", 0x5d), CHAR_REF("rsquo;", 0x2019), CHAR_REF("rsquor;", 0x2019), CHAR_REF("rthree;", 0x22cc), CHAR_REF("rtimes;", 0x22ca), CHAR_REF("rtri;", 0x25b9), CHAR_REF("rtrie;", 0x22b5), CHAR_REF("rtrif;", 0x25b8), CHAR_REF("rtriltri;", 0x29ce), CHAR_REF("ruluhar;", 0x2968), CHAR_REF("rx;", 0x211e), CHAR_REF("sacute;", 0x015b), CHAR_REF("sbquo;", 0x201a), CHAR_REF("sc;", 0x227b), CHAR_REF("scE;", 0x2ab4), CHAR_REF("scap;", 0x2ab8), CHAR_REF("scaron;", 0x0161), CHAR_REF("sccue;", 0x227d), CHAR_REF("sce;", 0x2ab0), CHAR_REF("scedil;", 0x015f), CHAR_REF("scirc;", 0x015d), CHAR_REF("scnE;", 0x2ab6), CHAR_REF("scnap;", 0x2aba), CHAR_REF("scnsim;", 0x22e9), CHAR_REF("scpolint;", 0x2a13), CHAR_REF("scsim;", 0x227f), CHAR_REF("scy;", 0x0441), CHAR_REF("sdot;", 0x22c5), CHAR_REF("sdotb;", 0x22a1), CHAR_REF("sdote;", 0x2a66), CHAR_REF("seArr;", 0x21d8), CHAR_REF("searhk;", 0x2925), CHAR_REF("searr;", 0x2198), CHAR_REF("searrow;", 0x2198), CHAR_REF("sect;", 0xa7), CHAR_REF("sect", 0xa7), CHAR_REF("semi;", 0x3b), CHAR_REF("seswar;", 0x2929), CHAR_REF("setminus;", 0x2216), CHAR_REF("setmn;", 0x2216), CHAR_REF("sext;", 0x2736), CHAR_REF("sfr;", 0x0001d530), CHAR_REF("sfrown;", 0x2322), CHAR_REF("sharp;", 0x266f), CHAR_REF("shchcy;", 0x0449), CHAR_REF("shcy;", 0x0448), CHAR_REF("shortmid;", 0x2223), CHAR_REF("shortparallel;", 0x2225), CHAR_REF("shy;", 0xad), CHAR_REF("shy", 0xad), CHAR_REF("sigma;", 0x03c3), CHAR_REF("sigmaf;", 0x03c2), CHAR_REF("sigmav;", 0x03c2), CHAR_REF("sim;", 0x223c), CHAR_REF("simdot;", 0x2a6a), CHAR_REF("sime;", 0x2243), CHAR_REF("simeq;", 0x2243), CHAR_REF("simg;", 0x2a9e), CHAR_REF("simgE;", 0x2aa0), CHAR_REF("siml;", 0x2a9d), CHAR_REF("simlE;", 0x2a9f), CHAR_REF("simne;", 0x2246), CHAR_REF("simplus;", 0x2a24), CHAR_REF("simrarr;", 0x2972), CHAR_REF("slarr;", 0x2190), CHAR_REF("smallsetminus;", 0x2216), CHAR_REF("smashp;", 0x2a33), CHAR_REF("smeparsl;", 0x29e4), CHAR_REF("smid;", 0x2223), CHAR_REF("smile;", 0x2323), CHAR_REF("smt;", 0x2aaa), CHAR_REF("smte;", 0x2aac), MULTI_CHAR_REF("smtes;", 0x2aac, 0xfe00), CHAR_REF("softcy;", 0x044c), CHAR_REF("sol;", 0x2f), CHAR_REF("solb;", 0x29c4), CHAR_REF("solbar;", 0x233f), CHAR_REF("sopf;", 0x0001d564), CHAR_REF("spades;", 0x2660), CHAR_REF("spadesuit;", 0x2660), CHAR_REF("spar;", 0x2225), CHAR_REF("sqcap;", 0x2293), MULTI_CHAR_REF("sqcaps;", 0x2293, 0xfe00), CHAR_REF("sqcup;", 0x2294), MULTI_CHAR_REF("sqcups;", 0x2294, 0xfe00), CHAR_REF("sqsub;", 0x228f), CHAR_REF("sqsube;", 0x2291), CHAR_REF("sqsubset;", 0x228f), CHAR_REF("sqsubseteq;", 0x2291), CHAR_REF("sqsup;", 0x2290), CHAR_REF("sqsupe;", 0x2292), CHAR_REF("sqsupset;", 0x2290), CHAR_REF("sqsupseteq;", 0x2292), CHAR_REF("squ;", 0x25a1), CHAR_REF("square;", 0x25a1), CHAR_REF("squarf;", 0x25aa), CHAR_REF("squf;", 0x25aa), CHAR_REF("srarr;", 0x2192), CHAR_REF("sscr;", 0x0001d4c8), CHAR_REF("ssetmn;", 0x2216), CHAR_REF("ssmile;", 0x2323), CHAR_REF("sstarf;", 0x22c6), CHAR_REF("star;", 0x2606), CHAR_REF("starf;", 0x2605), CHAR_REF("straightepsilon;", 0x03f5), CHAR_REF("straightphi;", 0x03d5), CHAR_REF("strns;", 0xaf), CHAR_REF("sub;", 0x2282), CHAR_REF("subE;", 0x2ac5), CHAR_REF("subdot;", 0x2abd), CHAR_REF("sube;", 0x2286), CHAR_REF("subedot;", 0x2ac3), CHAR_REF("submult;", 0x2ac1), CHAR_REF("subnE;", 0x2acb), CHAR_REF("subne;", 0x228a), CHAR_REF("subplus;", 0x2abf), CHAR_REF("subrarr;", 0x2979), CHAR_REF("subset;", 0x2282), CHAR_REF("subseteq;", 0x2286), CHAR_REF("subseteqq;", 0x2ac5), CHAR_REF("subsetneq;", 0x228a), CHAR_REF("subsetneqq;", 0x2acb), CHAR_REF("subsim;", 0x2ac7), CHAR_REF("subsub;", 0x2ad5), CHAR_REF("subsup;", 0x2ad3), CHAR_REF("succ;", 0x227b), CHAR_REF("succapprox;", 0x2ab8), CHAR_REF("succcurlyeq;", 0x227d), CHAR_REF("succeq;", 0x2ab0), CHAR_REF("succnapprox;", 0x2aba), CHAR_REF("succneqq;", 0x2ab6), CHAR_REF("succnsim;", 0x22e9), CHAR_REF("succsim;", 0x227f), CHAR_REF("sum;", 0x2211), CHAR_REF("sung;", 0x266a), CHAR_REF("sup1;", 0xb9), CHAR_REF("sup1", 0xb9), CHAR_REF("sup2;", 0xb2), CHAR_REF("sup2", 0xb2), CHAR_REF("sup3;", 0xb3), CHAR_REF("sup3", 0xb3), CHAR_REF("sup;", 0x2283), CHAR_REF("supE;", 0x2ac6), CHAR_REF("supdot;", 0x2abe), CHAR_REF("supdsub;", 0x2ad8), CHAR_REF("supe;", 0x2287), CHAR_REF("supedot;", 0x2ac4), CHAR_REF("suphsol;", 0x27c9), CHAR_REF("suphsub;", 0x2ad7), CHAR_REF("suplarr;", 0x297b), CHAR_REF("supmult;", 0x2ac2), CHAR_REF("supnE;", 0x2acc), CHAR_REF("supne;", 0x228b), CHAR_REF("supplus;", 0x2ac0), CHAR_REF("supset;", 0x2283), CHAR_REF("supseteq;", 0x2287), CHAR_REF("supseteqq;", 0x2ac6), CHAR_REF("supsetneq;", 0x228b), CHAR_REF("supsetneqq;", 0x2acc), CHAR_REF("supsim;", 0x2ac8), CHAR_REF("supsub;", 0x2ad4), CHAR_REF("supsup;", 0x2ad6), CHAR_REF("swArr;", 0x21d9), CHAR_REF("swarhk;", 0x2926), CHAR_REF("swarr;", 0x2199), CHAR_REF("swarrow;", 0x2199), CHAR_REF("swnwar;", 0x292a), CHAR_REF("szlig;", 0xdf), CHAR_REF("szlig", 0xdf), CHAR_REF("target;", 0x2316), CHAR_REF("tau;", 0x03c4), CHAR_REF("tbrk;", 0x23b4), CHAR_REF("tcaron;", 0x0165), CHAR_REF("tcedil;", 0x0163), CHAR_REF("tcy;", 0x0442), CHAR_REF("tdot;", 0x20db), CHAR_REF("telrec;", 0x2315), CHAR_REF("tfr;", 0x0001d531), CHAR_REF("there4;", 0x2234), CHAR_REF("therefore;", 0x2234), CHAR_REF("theta;", 0x03b8), CHAR_REF("thetasym;", 0x03d1), CHAR_REF("thetav;", 0x03d1), CHAR_REF("thickapprox;", 0x2248), CHAR_REF("thicksim;", 0x223c), CHAR_REF("thinsp;", 0x2009), CHAR_REF("thkap;", 0x2248), CHAR_REF("thksim;", 0x223c), CHAR_REF("thorn;", 0xfe), CHAR_REF("thorn", 0xfe), CHAR_REF("tilde;", 0x02dc), CHAR_REF("times;", 0xd7), CHAR_REF("times", 0xd7), CHAR_REF("timesb;", 0x22a0), CHAR_REF("timesbar;", 0x2a31), CHAR_REF("timesd;", 0x2a30), CHAR_REF("tint;", 0x222d), CHAR_REF("toea;", 0x2928), CHAR_REF("top;", 0x22a4), CHAR_REF("topbot;", 0x2336), CHAR_REF("topcir;", 0x2af1), CHAR_REF("topf;", 0x0001d565), CHAR_REF("topfork;", 0x2ada), CHAR_REF("tosa;", 0x2929), CHAR_REF("tprime;", 0x2034), CHAR_REF("trade;", 0x2122), CHAR_REF("triangle;", 0x25b5), CHAR_REF("triangledown;", 0x25bf), CHAR_REF("triangleleft;", 0x25c3), CHAR_REF("trianglelefteq;", 0x22b4), CHAR_REF("triangleq;", 0x225c), CHAR_REF("triangleright;", 0x25b9), CHAR_REF("trianglerighteq;", 0x22b5), CHAR_REF("tridot;", 0x25ec), CHAR_REF("trie;", 0x225c), CHAR_REF("triminus;", 0x2a3a), CHAR_REF("triplus;", 0x2a39), CHAR_REF("trisb;", 0x29cd), CHAR_REF("tritime;", 0x2a3b), CHAR_REF("trpezium;", 0x23e2), CHAR_REF("tscr;", 0x0001d4c9), CHAR_REF("tscy;", 0x0446), CHAR_REF("tshcy;", 0x045b), CHAR_REF("tstrok;", 0x0167), CHAR_REF("twixt;", 0x226c), CHAR_REF("twoheadleftarrow;", 0x219e), CHAR_REF("twoheadrightarrow;", 0x21a0), CHAR_REF("uArr;", 0x21d1), CHAR_REF("uHar;", 0x2963), CHAR_REF("uacute;", 0xfa), CHAR_REF("uacute", 0xfa), CHAR_REF("uarr;", 0x2191), CHAR_REF("ubrcy;", 0x045e), CHAR_REF("ubreve;", 0x016d), CHAR_REF("ucirc;", 0xfb), CHAR_REF("ucirc", 0xfb), CHAR_REF("ucy;", 0x0443), CHAR_REF("udarr;", 0x21c5), CHAR_REF("udblac;", 0x0171), CHAR_REF("udhar;", 0x296e), CHAR_REF("ufisht;", 0x297e), CHAR_REF("ufr;", 0x0001d532), CHAR_REF("ugrave;", 0xf9), CHAR_REF("ugrave", 0xf9), CHAR_REF("uharl;", 0x21bf), CHAR_REF("uharr;", 0x21be), CHAR_REF("uhblk;", 0x2580), CHAR_REF("ulcorn;", 0x231c), CHAR_REF("ulcorner;", 0x231c), CHAR_REF("ulcrop;", 0x230f), CHAR_REF("ultri;", 0x25f8), CHAR_REF("umacr;", 0x016b), CHAR_REF("uml;", 0xa8), CHAR_REF("uml", 0xa8), CHAR_REF("uogon;", 0x0173), CHAR_REF("uopf;", 0x0001d566), CHAR_REF("uparrow;", 0x2191), CHAR_REF("updownarrow;", 0x2195), CHAR_REF("upharpoonleft;", 0x21bf), CHAR_REF("upharpoonright;", 0x21be), CHAR_REF("uplus;", 0x228e), CHAR_REF("upsi;", 0x03c5), CHAR_REF("upsih;", 0x03d2), CHAR_REF("upsilon;", 0x03c5), CHAR_REF("upuparrows;", 0x21c8), CHAR_REF("urcorn;", 0x231d), CHAR_REF("urcorner;", 0x231d), CHAR_REF("urcrop;", 0x230e), CHAR_REF("uring;", 0x016f), CHAR_REF("urtri;", 0x25f9), CHAR_REF("uscr;", 0x0001d4ca), CHAR_REF("utdot;", 0x22f0), CHAR_REF("utilde;", 0x0169), CHAR_REF("utri;", 0x25b5), CHAR_REF("utrif;", 0x25b4), CHAR_REF("uuarr;", 0x21c8), CHAR_REF("uuml;", 0xfc), CHAR_REF("uuml", 0xfc), CHAR_REF("uwangle;", 0x29a7), CHAR_REF("vArr;", 0x21d5), CHAR_REF("vBar;", 0x2ae8), CHAR_REF("vBarv;", 0x2ae9), CHAR_REF("vDash;", 0x22a8), CHAR_REF("vangrt;", 0x299c), CHAR_REF("varepsilon;", 0x03f5), CHAR_REF("varkappa;", 0x03f0), CHAR_REF("varnothing;", 0x2205), CHAR_REF("varphi;", 0x03d5), CHAR_REF("varpi;", 0x03d6), CHAR_REF("varpropto;", 0x221d), CHAR_REF("varr;", 0x2195), CHAR_REF("varrho;", 0x03f1), CHAR_REF("varsigma;", 0x03c2), MULTI_CHAR_REF("varsubsetneq;", 0x228a, 0xfe00), MULTI_CHAR_REF("varsubsetneqq;", 0x2acb, 0xfe00), MULTI_CHAR_REF("varsupsetneq;", 0x228b, 0xfe00), MULTI_CHAR_REF("varsupsetneqq;", 0x2acc, 0xfe00), CHAR_REF("vartheta;", 0x03d1), CHAR_REF("vartriangleleft;", 0x22b2), CHAR_REF("vartriangleright;", 0x22b3), CHAR_REF("vcy;", 0x0432), CHAR_REF("vdash;", 0x22a2), CHAR_REF("vee;", 0x2228), CHAR_REF("veebar;", 0x22bb), CHAR_REF("veeeq;", 0x225a), CHAR_REF("vellip;", 0x22ee), CHAR_REF("verbar;", 0x7c), CHAR_REF("vert;", 0x7c), CHAR_REF("vfr;", 0x0001d533), CHAR_REF("vltri;", 0x22b2), MULTI_CHAR_REF("vnsub;", 0x2282, 0x20d2), MULTI_CHAR_REF("vnsup;", 0x2283, 0x20d2), CHAR_REF("vopf;", 0x0001d567), CHAR_REF("vprop;", 0x221d), CHAR_REF("vrtri;", 0x22b3), CHAR_REF("vscr;", 0x0001d4cb), MULTI_CHAR_REF("vsubnE;", 0x2acb, 0xfe00), MULTI_CHAR_REF("vsubne;", 0x228a, 0xfe00), MULTI_CHAR_REF("vsupnE;", 0x2acc, 0xfe00), MULTI_CHAR_REF("vsupne;", 0x228b, 0xfe00), CHAR_REF("vzigzag;", 0x299a), CHAR_REF("wcirc;", 0x0175), CHAR_REF("wedbar;", 0x2a5f), CHAR_REF("wedge;", 0x2227), CHAR_REF("wedgeq;", 0x2259), CHAR_REF("weierp;", 0x2118), CHAR_REF("wfr;", 0x0001d534), CHAR_REF("wopf;", 0x0001d568), CHAR_REF("wp;", 0x2118), CHAR_REF("wr;", 0x2240), CHAR_REF("wreath;", 0x2240), CHAR_REF("wscr;", 0x0001d4cc), CHAR_REF("xcap;", 0x22c2), CHAR_REF("xcirc;", 0x25ef), CHAR_REF("xcup;", 0x22c3), CHAR_REF("xdtri;", 0x25bd), CHAR_REF("xfr;", 0x0001d535), CHAR_REF("xhArr;", 0x27fa), CHAR_REF("xharr;", 0x27f7), CHAR_REF("xi;", 0x03be), CHAR_REF("xlArr;", 0x27f8), CHAR_REF("xlarr;", 0x27f5), CHAR_REF("xmap;", 0x27fc), CHAR_REF("xnis;", 0x22fb), CHAR_REF("xodot;", 0x2a00), CHAR_REF("xopf;", 0x0001d569), CHAR_REF("xoplus;", 0x2a01), CHAR_REF("xotime;", 0x2a02), CHAR_REF("xrArr;", 0x27f9), CHAR_REF("xrarr;", 0x27f6), CHAR_REF("xscr;", 0x0001d4cd), CHAR_REF("xsqcup;", 0x2a06), CHAR_REF("xuplus;", 0x2a04), CHAR_REF("xutri;", 0x25b3), CHAR_REF("xvee;", 0x22c1), CHAR_REF("xwedge;", 0x22c0), CHAR_REF("yacute;", 0xfd), CHAR_REF("yacute", 0xfd), CHAR_REF("yacy;", 0x044f), CHAR_REF("ycirc;", 0x0177), CHAR_REF("ycy;", 0x044b), CHAR_REF("yen;", 0xa5), CHAR_REF("yen", 0xa5), CHAR_REF("yfr;", 0x0001d536), CHAR_REF("yicy;", 0x0457), CHAR_REF("yopf;", 0x0001d56a), CHAR_REF("yscr;", 0x0001d4ce), CHAR_REF("yucy;", 0x044e), CHAR_REF("yuml;", 0xff), CHAR_REF("yuml", 0xff), CHAR_REF("zacute;", 0x017a), CHAR_REF("zcaron;", 0x017e), CHAR_REF("zcy;", 0x0437), CHAR_REF("zdot;", 0x017c), CHAR_REF("zeetrf;", 0x2128), CHAR_REF("zeta;", 0x03b6), CHAR_REF("zfr;", 0x0001d537), CHAR_REF("zhcy;", 0x0436), CHAR_REF("zigrarr;", 0x21dd), CHAR_REF("zopf;", 0x0001d56b), CHAR_REF("zscr;", 0x0001d4cf), CHAR_REF("zwj;", 0x200d), CHAR_REF("zwnj;", 0x200c), // Terminator. CHAR_REF("", -1) }; // Table of replacement characters. The spec specifies that any occurrence of // the first character should be replaced by the second character, and a parse // error recorded. typedef struct { int from_char; int to_char; } CharReplacement; static const CharReplacement kCharReplacements[] = { { 0x00, 0xfffd }, { 0x0d, 0x000d }, { 0x80, 0x20ac }, { 0x81, 0x0081 }, { 0x82, 0x201A }, { 0x83, 0x0192 }, { 0x84, 0x201E }, { 0x85, 0x2026 }, { 0x86, 0x2020 }, { 0x87, 0x2021 }, { 0x88, 0x02C6 }, { 0x89, 0x2030 }, { 0x8A, 0x0160 }, { 0x8B, 0x2039 }, { 0x8C, 0x0152 }, { 0x8D, 0x008D }, { 0x8E, 0x017D }, { 0x8F, 0x008F }, { 0x90, 0x0090 }, { 0x91, 0x2018 }, { 0x92, 0x2019 }, { 0x93, 0x201C }, { 0x94, 0x201D }, { 0x95, 0x2022 }, { 0x96, 0x2013 }, { 0x97, 0x2014 }, { 0x98, 0x02DC }, { 0x99, 0x2122 }, { 0x9A, 0x0161 }, { 0x9B, 0x203A }, { 0x9C, 0x0153 }, { 0x9D, 0x009D }, { 0x9E, 0x017E }, { 0x9F, 0x0178 }, // Terminator. { -1, -1 } }; static int parse_digit(int c, bool allow_hex) { if (c >= '0' && c <= '9') { return c - '0'; } if (allow_hex && c >= 'a' && c <= 'f') { return c - 'a' + 10; } if (allow_hex && c >= 'A' && c <= 'F') { return c - 'A' + 10; } return -1; } static void add_no_digit_error( struct GumboInternalParser* parser, Utf8Iterator* input) { GumboError* error = gumbo_add_error(parser); if (!error) { return; } utf8iterator_fill_error_at_mark(input, error); error->type = GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS; } static void add_codepoint_error( struct GumboInternalParser* parser, Utf8Iterator* input, GumboErrorType type, int codepoint) { GumboError* error = gumbo_add_error(parser); if (!error) { return; } utf8iterator_fill_error_at_mark(input, error); error->type = type; error->v.codepoint = codepoint; } static void add_named_reference_error( struct GumboInternalParser* parser, Utf8Iterator* input, GumboErrorType type, GumboStringPiece text) { GumboError* error = gumbo_add_error(parser); if (!error) { return; } utf8iterator_fill_error_at_mark(input, error); error->type = type; error->v.text = text; } static int maybe_replace_codepoint(int codepoint) { for (int i = 0; kCharReplacements[i].from_char != -1; ++i) { if (kCharReplacements[i].from_char == codepoint) { return kCharReplacements[i].to_char; } } return -1; } static bool consume_numeric_ref( struct GumboInternalParser* parser, Utf8Iterator* input, int* output) { utf8iterator_next(input); bool is_hex = false; int c = utf8iterator_current(input); if (c == 'x' || c == 'X') { is_hex = true; utf8iterator_next(input); c = utf8iterator_current(input); } int digit = parse_digit(c, is_hex); if (digit == -1) { // First digit was invalid; add a parse error and return. add_no_digit_error(parser, input); utf8iterator_reset(input); *output = kGumboNoChar; return false; } int codepoint = 0; bool status = true; do { codepoint = (codepoint * (is_hex ? 16 : 10)) + digit; utf8iterator_next(input); digit = parse_digit(utf8iterator_current(input), is_hex); } while (digit != -1); if (utf8iterator_current(input) != ';') { add_codepoint_error( parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON, codepoint); status = false; } else { utf8iterator_next(input); } int replacement = maybe_replace_codepoint(codepoint); if (replacement != -1) { add_codepoint_error( parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); *output = replacement; return false; } if ((codepoint >= 0xd800 && codepoint <= 0xdfff) || codepoint > 0x10ffff) { add_codepoint_error( parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); *output = 0xfffd; return false; } if (utf8_is_invalid_code_point(codepoint) || codepoint == 0xb) { add_codepoint_error( parser, input, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, codepoint); status = false; // But return it anyway, per spec. } *output = codepoint; return status; } static const NamedCharRef* find_named_char_ref(Utf8Iterator* input) { for (int i = 0; kNamedEntities[i].codepoints.first != -1; ++i) { const NamedCharRef* current = &kNamedEntities[i]; assert(strlen(current->name) == current->length); if (utf8iterator_maybe_consume_match( input, current->name, current->length, true)) { assert(current->name != NULL); assert(current->length > 0); assert(current->codepoints.first != kGumboNoChar); return current; } } return NULL; } static bool is_legal_attribute_char_next(Utf8Iterator* input) { int c = utf8iterator_current(input); return c == '=' || isalnum(c); } static bool maybe_add_invalid_named_reference( struct GumboInternalParser* parser, Utf8Iterator* input) { // The iterator will always be reset in this code path, so we don't need to // worry about consuming characters. const char* start = utf8iterator_get_char_pointer(input); int c = utf8iterator_current(input); while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { utf8iterator_next(input); c = utf8iterator_current(input); } if (c == ';') { GumboStringPiece bad_ref; bad_ref.data = start; bad_ref.length = utf8iterator_get_char_pointer(input) - start; add_named_reference_error( parser, input, GUMBO_ERR_NAMED_CHAR_REF_INVALID, bad_ref); return false; } return true; } static bool consume_named_ref( struct GumboInternalParser* parser, Utf8Iterator* input, bool is_in_attribute, OneOrTwoCodepoints* output) { assert(output->first == kGumboNoChar); const NamedCharRef* char_ref = find_named_char_ref(input); if (char_ref) { assert(char_ref->length == strlen(char_ref->name)); char last_char = char_ref->name[char_ref->length - 1]; if (last_char == ';') { *output = char_ref->codepoints; assert(output->first != kGumboNoChar); return true; } else if (is_in_attribute && is_legal_attribute_char_next(input)) { utf8iterator_reset(input); return true; } else { GumboStringPiece bad_ref; bad_ref.data = char_ref->name; bad_ref.length = char_ref->length; add_named_reference_error( parser, input, GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON, bad_ref); *output = char_ref->codepoints; assert(output->first != kGumboNoChar); return false; } } else { bool status = maybe_add_invalid_named_reference(parser, input); utf8iterator_reset(input); return status; } } bool consume_char_ref( struct GumboInternalParser* parser, struct GumboInternalUtf8Iterator* input, int additional_allowed_char, bool is_in_attribute, OneOrTwoCodepoints* output) { utf8iterator_mark(input); utf8iterator_next(input); int c = utf8iterator_current(input); output->first = kGumboNoChar; output->second = kGumboNoChar; if (c == additional_allowed_char) { utf8iterator_reset(input); output->first = kGumboNoChar; return true; } switch (utf8iterator_current(input)) { case '\t': case '\n': case '\f': case ' ': case '<': case '&': case -1: utf8iterator_reset(input); return true; case '#': return consume_numeric_ref(parser, input, &output->first); default: return consume_named_ref(parser, input, is_in_attribute, output); } } ================================================ FILE: Pod/Classes/Private/Gumbo/char_ref.h ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) // // Internal header for character reference handling; this should not be exposed // transitively by any public API header. This is why the functions aren't // namespaced. #ifndef GUMBO_CHAR_REF_H_ #define GUMBO_CHAR_REF_H_ #include #ifdef __cplusplus extern "C" { #endif struct GumboInternalParser; struct GumboInternalUtf8Iterator; // Value that indicates no character was produced. extern const int kGumboNoChar; // Certain named character references generate two codepoints, not one, and so // the consume_char_ref subroutine needs to return this instead of an int. The // first field will be kGumboNoChar if no character reference was found; the // second field will be kGumboNoChar if that is the case or if the character // reference returns only a single codepoint. typedef struct { int first; int second; } OneOrTwoCodepoints; // Implements the "consume a character reference" section of the spec. // This reads in characters from the input as necessary, and fills in a // OneOrTwoCodepoints struct containing the characters read. It may add parse // errors to the GumboParser's errors vector, if the spec calls for it. Pass a // space for the "additional allowed char" when the spec says "with no // additional allowed char". Returns false on parse error, true otherwise. bool consume_char_ref( struct GumboInternalParser* parser, struct GumboInternalUtf8Iterator* input, int additional_allowed_char, bool is_in_attribute, OneOrTwoCodepoints* output); #ifdef __cplusplus } #endif #endif // GUMBO_CHAR_REF_H_ ================================================ FILE: Pod/Classes/Private/Gumbo/error.c ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #include "error.h" #include #include #include #include #include "gumbo.h" #include "parser.h" #include "string_buffer.h" #include "util.h" #include "vector.h" //static const size_t kMessageBufferSize = 256; // Prints a formatted message to a StringBuffer. This automatically resizes the // StringBuffer as necessary to fit the message. Returns the number of bytes // written. static int print_message(GumboParser* parser, GumboStringBuffer* output, const char* format, ...) { va_list args; va_start(args, format); unsigned long remaining_capacity = output->capacity - output->length; int bytes_written = vsnprintf(output->data + output->length, remaining_capacity, format, args); if (bytes_written > remaining_capacity) { gumbo_string_buffer_reserve( parser, output->capacity + bytes_written, output); remaining_capacity = output->capacity - output->length; bytes_written = vsnprintf(output->data + output->length, remaining_capacity, format, args); } output->length += bytes_written; va_end(args); return bytes_written; } static void print_tag_stack( GumboParser* parser, const GumboParserError* error, GumboStringBuffer* output) { print_message(parser, output, " Currently open tags: "); for (int i = 0; i < error->tag_stack.length; ++i) { if (i) { print_message(parser, output, ", "); } GumboTag tag = (GumboTag) error->tag_stack.data[i]; print_message(parser, output, gumbo_normalized_tagname(tag)); } gumbo_string_buffer_append_codepoint(parser, '.', output); } static void handle_parser_error(GumboParser* parser, const GumboParserError* error, GumboStringBuffer* output) { if (error->parser_state == GUMBO_INSERTION_MODE_INITIAL && error->input_type != GUMBO_TOKEN_DOCTYPE) { print_message(parser, output, "The doctype must be the first token in the document"); return; } switch (error->input_type) { case GUMBO_TOKEN_DOCTYPE: print_message(parser, output, "This is not a legal doctype"); return; case GUMBO_TOKEN_COMMENT: // Should never happen; comments are always legal. assert(0); // But just in case... print_message(parser, output, "Comments aren't legal here"); return; case GUMBO_TOKEN_WHITESPACE: case GUMBO_TOKEN_CHARACTER: print_message(parser, output, "Character tokens aren't legal here"); return; case GUMBO_TOKEN_NULL: print_message(parser, output, "Null bytes are not allowed in HTML5"); return; case GUMBO_TOKEN_EOF: if (error->parser_state == GUMBO_INSERTION_MODE_INITIAL) { print_message(parser, output, "You must provide a doctype"); } else { print_message(parser, output, "Premature end of file"); print_tag_stack(parser, error, output); } return; case GUMBO_TOKEN_START_TAG: case GUMBO_TOKEN_END_TAG: print_message(parser, output, "That tag isn't allowed here"); print_tag_stack(parser, error, output); // TODO(jdtang): Give more specific messaging. return; } } // Finds the preceding newline in an original source buffer from a given byte // location. Returns a character pointer to the character after that, or a // pointer to the beginning of the string if this is the first line. static const char* find_last_newline( const char* original_text, const char* error_location) { assert(error_location >= original_text); const char* c = error_location; for (; c != original_text && *c != '\n'; --c) { // There may be an error at EOF, which would be a nul byte. assert(*c || c == error_location); } return c == original_text ? c : c + 1; } // Finds the next newline in the original source buffer from a given byte // location. Returns a character pointer to that newline, or a pointer to the // terminating null byte if this is the last line. static const char* find_next_newline( const char* original_text, const char* error_location) { const char* c = error_location; for (; *c && *c != '\n'; ++c); return c; } GumboError* gumbo_add_error(GumboParser* parser) { int max_errors = parser->_options->max_errors; if (max_errors < 0 && parser->_output->errors.length >= max_errors) { return NULL; } GumboError* error = gumbo_parser_allocate(parser, sizeof(GumboError)); gumbo_vector_add(parser, error, &parser->_output->errors); return error; } void gumbo_error_to_string( GumboParser* parser, const GumboError* error, GumboStringBuffer* output) { print_message(parser, output, "@%d:%d: ", error->position.line, error->position.column); switch (error->type) { case GUMBO_ERR_UTF8_INVALID: print_message(parser, output, "Invalid UTF8 character 0x%x", error->v.codepoint); break; case GUMBO_ERR_UTF8_TRUNCATED: print_message(parser, output, "Input stream ends with a truncated UTF8 character 0x%x", error->v.codepoint); break; case GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS: print_message(parser, output, "No digits after &# in numeric character reference"); break; case GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON: print_message(parser, output, "The numeric character reference &#%d should be followed " "by a semicolon", error->v.codepoint); break; case GUMBO_ERR_NUMERIC_CHAR_REF_INVALID: print_message(parser, output, "The numeric character reference &#%d; encodes an invalid " "unicode codepoint", error->v.codepoint); break; case GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON: // The textual data came from one of the literal strings in the table, and // so it'll be null-terminated. print_message(parser, output, "The named character reference &%.*s should be followed by a " "semicolon", (int) error->v.text.length, error->v.text.data); break; case GUMBO_ERR_NAMED_CHAR_REF_INVALID: print_message(parser, output, "The named character reference &%.*s; is not a valid entity name", (int) error->v.text.length, error->v.text.data); break; case GUMBO_ERR_DUPLICATE_ATTR: print_message(parser, output, "Attribute %s occurs multiple times, at positions %d and %d", error->v.duplicate_attr.name, error->v.duplicate_attr.original_index, error->v.duplicate_attr.new_index); break; case GUMBO_ERR_PARSER: case GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG: handle_parser_error(parser, &error->v.parser, output); break; default: print_message(parser, output, "Tokenizer error with an unimplemented error message"); break; } gumbo_string_buffer_append_codepoint(parser, '.', output); } void gumbo_caret_diagnostic_to_string( GumboParser* parser, const GumboError* error, const char* source_text, GumboStringBuffer* output) { gumbo_error_to_string(parser, error, output); const char* line_start = find_last_newline(source_text, error->original_text); const char* line_end = find_next_newline(source_text, error->original_text); GumboStringPiece original_line; original_line.data = line_start; original_line.length = line_end - line_start; gumbo_string_buffer_append_codepoint(parser, '\n', output); gumbo_string_buffer_append_string(parser, &original_line, output); gumbo_string_buffer_append_codepoint(parser, '\n', output); gumbo_string_buffer_reserve( parser, output->length + error->position.column, output); int num_spaces = error->position.column - 1; memset(output->data + output->length, ' ', num_spaces); output->length += num_spaces; gumbo_string_buffer_append_codepoint(parser, '^', output); gumbo_string_buffer_append_codepoint(parser, '\n', output); } void gumbo_print_caret_diagnostic( GumboParser* parser, const GumboError* error, const char* source_text) { GumboStringBuffer text; gumbo_string_buffer_init(parser, &text); gumbo_caret_diagnostic_to_string(parser, error, source_text, &text); printf("%.*s", (int) text.length, text.data); gumbo_string_buffer_destroy(parser, &text); } void gumbo_error_destroy(GumboParser* parser, GumboError* error) { if (error->type == GUMBO_ERR_PARSER || error->type == GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG) { gumbo_vector_destroy(parser, &error->v.parser.tag_stack); } else if (error->type == GUMBO_ERR_DUPLICATE_ATTR) { gumbo_parser_deallocate(parser, (void*) error->v.duplicate_attr.name); } gumbo_parser_deallocate(parser, error); } void gumbo_init_errors(GumboParser* parser) { gumbo_vector_init(parser, 5, &parser->_output->errors); } void gumbo_destroy_errors(GumboParser* parser) { for (int i = 0; i < parser->_output->errors.length; ++i) { gumbo_error_destroy(parser, parser->_output->errors.data[i]); } gumbo_vector_destroy(parser, &parser->_output->errors); } ================================================ FILE: Pod/Classes/Private/Gumbo/error.h ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) // // Error types, enums, and handling functions. #ifndef GUMBO_ERROR_H_ #define GUMBO_ERROR_H_ #include #include "gumbo.h" #include "insertion_mode.h" #include "string_buffer.h" #include "token_type.h" #ifdef __cplusplus extern "C" { #endif struct GumboInternalParser; typedef enum { GUMBO_ERR_UTF8_INVALID, GUMBO_ERR_UTF8_TRUNCATED, GUMBO_ERR_UTF8_NULL, GUMBO_ERR_NUMERIC_CHAR_REF_NO_DIGITS, GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON, GUMBO_ERR_NUMERIC_CHAR_REF_INVALID, GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON, GUMBO_ERR_NAMED_CHAR_REF_INVALID, GUMBO_ERR_TAG_STARTS_WITH_QUESTION, GUMBO_ERR_TAG_EOF, GUMBO_ERR_TAG_INVALID, GUMBO_ERR_CLOSE_TAG_EMPTY, GUMBO_ERR_CLOSE_TAG_EOF, GUMBO_ERR_CLOSE_TAG_INVALID, GUMBO_ERR_SCRIPT_EOF, GUMBO_ERR_ATTR_NAME_EOF, GUMBO_ERR_ATTR_NAME_INVALID, GUMBO_ERR_ATTR_DOUBLE_QUOTE_EOF, GUMBO_ERR_ATTR_SINGLE_QUOTE_EOF, GUMBO_ERR_ATTR_UNQUOTED_EOF, GUMBO_ERR_ATTR_UNQUOTED_RIGHT_BRACKET, GUMBO_ERR_ATTR_UNQUOTED_EQUALS, GUMBO_ERR_ATTR_AFTER_EOF, GUMBO_ERR_ATTR_AFTER_INVALID, GUMBO_ERR_DUPLICATE_ATTR, GUMBO_ERR_SOLIDUS_EOF, GUMBO_ERR_SOLIDUS_INVALID, GUMBO_ERR_DASHES_OR_DOCTYPE, GUMBO_ERR_COMMENT_EOF, GUMBO_ERR_COMMENT_INVALID, GUMBO_ERR_COMMENT_BANG_AFTER_DOUBLE_DASH, GUMBO_ERR_COMMENT_DASH_AFTER_DOUBLE_DASH, GUMBO_ERR_COMMENT_SPACE_AFTER_DOUBLE_DASH, GUMBO_ERR_COMMENT_END_BANG_EOF, GUMBO_ERR_DOCTYPE_EOF, GUMBO_ERR_DOCTYPE_INVALID, GUMBO_ERR_DOCTYPE_SPACE, GUMBO_ERR_DOCTYPE_RIGHT_BRACKET, GUMBO_ERR_DOCTYPE_SPACE_OR_RIGHT_BRACKET, GUMBO_ERR_DOCTYPE_END, GUMBO_ERR_PARSER, GUMBO_ERR_UNACKNOWLEDGED_SELF_CLOSING_TAG, } GumboErrorType; // Additional data for duplicated attributes. typedef struct GumboInternalDuplicateAttrError { // The name of the attribute. Owned by this struct. const char* name; // The (0-based) index within the attributes vector of the original // occurrence. unsigned int original_index; // The (0-based) index where the new occurrence would be. unsigned int new_index; } GumboDuplicateAttrError; // A simplified representation of the tokenizer state, designed to be more // useful to clients of this library than the internal representation. This // condenses the actual states used in the tokenizer state machine into a few // values that will be familiar to users of HTML. typedef enum { GUMBO_ERR_TOKENIZER_DATA, GUMBO_ERR_TOKENIZER_CHAR_REF, GUMBO_ERR_TOKENIZER_RCDATA, GUMBO_ERR_TOKENIZER_RAWTEXT, GUMBO_ERR_TOKENIZER_PLAINTEXT, GUMBO_ERR_TOKENIZER_SCRIPT, GUMBO_ERR_TOKENIZER_TAG, GUMBO_ERR_TOKENIZER_SELF_CLOSING_TAG, GUMBO_ERR_TOKENIZER_ATTR_NAME, GUMBO_ERR_TOKENIZER_ATTR_VALUE, GUMBO_ERR_TOKENIZER_MARKUP_DECLARATION, GUMBO_ERR_TOKENIZER_COMMENT, GUMBO_ERR_TOKENIZER_DOCTYPE, GUMBO_ERR_TOKENIZER_CDATA, } GumboTokenizerErrorState; // Additional data for tokenizer errors. // This records the current state and codepoint encountered - this is usually // enough to reconstruct what went wrong and provide a friendly error message. typedef struct GumboInternalTokenizerError { // The bad codepoint encountered. int codepoint; // The state that the tokenizer was in at the time. GumboTokenizerErrorState state; } GumboTokenizerError; // Additional data for parse errors. typedef struct GumboInternalParserError { // The type of input token that resulted in this error. GumboTokenType input_type; // The HTML tag of the input token. TAG_UNKNOWN if this was not a tag token. GumboTag input_tag; // The insertion mode that the parser was in at the time. GumboInsertionMode parser_state; // The tag stack at the point of the error. Note that this is an GumboVector // of GumboTag's *stored by value* - cast the void* to an GumboTag directly to // get at the tag. GumboVector /* GumboTag */ tag_stack; } GumboParserError; // The overall error struct representing an error in decoding/tokenizing/parsing // the HTML. This contains an enumerated type flag, a source position, and then // a union of fields containing data specific to the error. typedef struct GumboInternalError { // The type of error. GumboErrorType type; // The position within the source file where the error occurred. GumboSourcePosition position; // A pointer to the byte within the original source file text where the error // occurred (note that this is not the same as position.offset, as that gives // character-based instead of byte-based offsets). const char* original_text; // Type-specific error information. union { // The code point we encountered, for: // * GUMBO_ERR_UTF8_INVALID // * GUMBO_ERR_UTF8_TRUNCATED // * GUMBO_ERR_NUMERIC_CHAR_REF_WITHOUT_SEMICOLON // * GUMBO_ERR_NUMERIC_CHAR_REF_INVALID uint64_t codepoint; // Tokenizer errors. GumboTokenizerError tokenizer; // Short textual data, for: // * GUMBO_ERR_NAMED_CHAR_REF_WITHOUT_SEMICOLON // * GUMBO_ERR_NAMED_CHAR_REF_INVALID GumboStringPiece text; // Duplicate attribute data, for GUMBO_ERR_DUPLICATE_ATTR. GumboDuplicateAttrError duplicate_attr; // Parser state, for GUMBO_ERR_PARSER and // GUMBO_ERR_UNACKNOWLEDGE_SELF_CLOSING_TAG. struct GumboInternalParserError parser; } v; } GumboError; // Adds a new error to the parser's error list, and returns a pointer to it so // that clients can fill out the rest of its fields. May return NULL if we're // already over the max_errors field specified in GumboOptions. GumboError* gumbo_add_error(struct GumboInternalParser* parser); // Initializes the errors vector in the parser. void gumbo_init_errors(struct GumboInternalParser* errors); // Frees all the errors in the 'errors_' field of the parser. void gumbo_destroy_errors(struct GumboInternalParser* errors); // Frees the memory used for a single GumboError. void gumbo_error_destroy(struct GumboInternalParser* parser, GumboError* error); // Prints an error to a string. This fills an empty GumboStringBuffer with a // freshly-allocated buffer containing the error message text. The caller is // responsible for deleting the buffer. (Note that the buffer is allocated with // the allocator specified in the GumboParser config and hence should be freed // by gumbo_parser_deallocate().) void gumbo_error_to_string( struct GumboInternalParser* parser, const GumboError* error, GumboStringBuffer* output); // Prints a caret diagnostic to a string. This fills an empty GumboStringBuffer // with a freshly-allocated buffer containing the error message text. The // caller is responsible for deleting the buffer. (Note that the buffer is // allocated with the allocator specified in the GumboParser config and hence // should be freed by gumbo_parser_deallocate().) void gumbo_caret_diagnostic_to_string( struct GumboInternalParser* parser, const GumboError* error, const char* source_text, GumboStringBuffer* output); // Like gumbo_caret_diagnostic_to_string, but prints the text to stdout instead // of writing to a string. void gumbo_print_caret_diagnostic( struct GumboInternalParser* parser, const GumboError* error, const char* source_text); #ifdef __cplusplus } #endif #endif // GUMBO_ERROR_H_ ================================================ FILE: Pod/Classes/Private/Gumbo/gumbo.h ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) // // We use Gumbo as a prefix for types, gumbo_ as a prefix for functions, and // GUMBO_ as a prefix for enum constants (static constants get the Google-style // kGumbo prefix). /** * @file * @mainpage Gumbo HTML Parser * * This provides a conformant, no-dependencies implementation of the HTML5 * parsing algorithm. It supports only UTF8; if you need to parse a different * encoding, run a preprocessing step to convert to UTF8. It returns a parse * tree made of the structs in this file. * * Example: * @code * GumboOutput* output = gumbo_parse(input); * do_something_with_doctype(output->document); * do_something_with_html_tree(output->root); * gumbo_destroy_output(&options, output); * @endcode * HTML5 Spec: * * http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html */ #ifndef GUMBO_GUMBO_H_ #define GUMBO_GUMBO_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** * A struct representing a character position within the original text buffer. * Line and column numbers are 1-based and offsets are 0-based, which matches * how most editors and command-line tools work. Also, columns measure * positions in terms of characters while offsets measure by bytes; this is * because the offset field is often used to pull out a particular region of * text (which in most languages that bind to C implies pointer arithmetic on a * buffer of bytes), while the column field is often used to reference a * particular column on a printable display, which nowadays is usually UTF-8. */ typedef struct { unsigned int line; unsigned int column; unsigned int offset; } GumboSourcePosition; /** * A SourcePosition used for elements that have no source position, i.e. * parser-inserted elements. */ extern const GumboSourcePosition kGumboEmptySourcePosition; /** * A struct representing a string or part of a string. Strings within the * parser are represented by a char* and a length; the char* points into * an existing data buffer owned by some other code (often the original input). * GumboStringPieces are assumed (by convention) to be immutable, because they * may share data. Use GumboStringBuffer if you need to construct a string. * Clients should assume that it is not NUL-terminated, and should always use * explicit lengths when manipulating them. */ typedef struct { /** A pointer to the beginning of the string. NULL iff length == 0. */ const char* data; /** The length of the string fragment, in bytes. May be zero. */ size_t length; } GumboStringPiece; /** A constant to represent a 0-length null string. */ extern const GumboStringPiece kGumboEmptyString; /** * Compares two GumboStringPieces, and returns true if they're equal or false * otherwise. */ bool gumbo_string_equals( const GumboStringPiece* str1, const GumboStringPiece* str2); /** * Compares two GumboStringPieces ignoring case, and returns true if they're * equal or false otherwise. */ bool gumbo_string_equals_ignore_case( const GumboStringPiece* str1, const GumboStringPiece* str2); /** * A simple vector implementation. This stores a pointer to a data array and a * length. All elements are stored as void*; client code must cast to the * appropriate type. Overflows upon addition result in reallocation of the data * array, with the size doubling to maintain O(1) amortized cost. There is no * removal function, as this isn't needed for any of the operations within this * library. Iteration can be done through inspecting the structure directly in * a for-loop. */ typedef struct { /** Data elements. This points to a dynamically-allocated array of capacity * elements, each a void* to the element itself. */ void** data; /** Number of elements currently in the vector. */ unsigned int length; /** Current array capacity. */ unsigned int capacity; } GumboVector; /** An empty (0-length, 0-capacity) GumboVector. */ extern const GumboVector kGumboEmptyVector; /** * Returns the first index at which an element appears in this vector (testing * by pointer equality), or -1 if it never does. */ int gumbo_vector_index_of(GumboVector* vector, void* element); /** * An enum for all the tags defined in the HTML5 standard. These correspond to * the tag names themselves. Enum constants exist only for tags which appear in * the spec itself (or for tags with special handling in the SVG and MathML * namespaces); any other tags appear as GUMBO_TAG_UNKNOWN and the actual tag * name can be obtained through original_tag. * * This is mostly for API convenience, so that clients of this library don't * need to perform a strcasecmp to find the normalized tag name. It also has * efficiency benefits, by letting the parser work with enums instead of * strings. */ typedef enum { // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#the-root-element GUMBO_TAG_HTML, // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#document-metadata GUMBO_TAG_HEAD, GUMBO_TAG_TITLE, GUMBO_TAG_BASE, GUMBO_TAG_LINK, GUMBO_TAG_META, GUMBO_TAG_STYLE, // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#scripting-1 GUMBO_TAG_SCRIPT, GUMBO_TAG_NOSCRIPT, // http://www.whatwg.org/specs/web-apps/current-work/multipage/sections.html#sections GUMBO_TAG_BODY, GUMBO_TAG_SECTION, GUMBO_TAG_NAV, GUMBO_TAG_ARTICLE, GUMBO_TAG_ASIDE, GUMBO_TAG_H1, GUMBO_TAG_H2, GUMBO_TAG_H3, GUMBO_TAG_H4, GUMBO_TAG_H5, GUMBO_TAG_H6, GUMBO_TAG_HGROUP, GUMBO_TAG_HEADER, GUMBO_TAG_FOOTER, GUMBO_TAG_ADDRESS, // http://www.whatwg.org/specs/web-apps/current-work/multipage/grouping-content.html#grouping-content GUMBO_TAG_P, GUMBO_TAG_HR, GUMBO_TAG_PRE, GUMBO_TAG_BLOCKQUOTE, GUMBO_TAG_OL, GUMBO_TAG_UL, GUMBO_TAG_LI, GUMBO_TAG_DL, GUMBO_TAG_DT, GUMBO_TAG_DD, GUMBO_TAG_FIGURE, GUMBO_TAG_FIGCAPTION, GUMBO_TAG_DIV, // http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#text-level-semantics GUMBO_TAG_A, GUMBO_TAG_EM, GUMBO_TAG_STRONG, GUMBO_TAG_SMALL, GUMBO_TAG_S, GUMBO_TAG_CITE, GUMBO_TAG_Q, GUMBO_TAG_DFN, GUMBO_TAG_ABBR, GUMBO_TAG_TIME, GUMBO_TAG_CODE, GUMBO_TAG_VAR, GUMBO_TAG_SAMP, GUMBO_TAG_KBD, GUMBO_TAG_SUB, GUMBO_TAG_SUP, GUMBO_TAG_I, GUMBO_TAG_B, GUMBO_TAG_MARK, GUMBO_TAG_RUBY, GUMBO_TAG_RT, GUMBO_TAG_RP, GUMBO_TAG_BDI, GUMBO_TAG_BDO, GUMBO_TAG_SPAN, GUMBO_TAG_BR, GUMBO_TAG_WBR, // http://www.whatwg.org/specs/web-apps/current-work/multipage/edits.html#edits GUMBO_TAG_INS, GUMBO_TAG_DEL, // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#embedded-content-1 GUMBO_TAG_IMAGE, GUMBO_TAG_IMG, GUMBO_TAG_IFRAME, GUMBO_TAG_EMBED, GUMBO_TAG_OBJECT, GUMBO_TAG_PARAM, GUMBO_TAG_VIDEO, GUMBO_TAG_AUDIO, GUMBO_TAG_SOURCE, GUMBO_TAG_TRACK, GUMBO_TAG_CANVAS, GUMBO_TAG_MAP, GUMBO_TAG_AREA, // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-map-element.html#mathml GUMBO_TAG_MATH, GUMBO_TAG_MI, GUMBO_TAG_MO, GUMBO_TAG_MN, GUMBO_TAG_MS, GUMBO_TAG_MTEXT, GUMBO_TAG_MGLYPH, GUMBO_TAG_MALIGNMARK, GUMBO_TAG_ANNOTATION_XML, // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-map-element.html#svg-0 GUMBO_TAG_SVG, GUMBO_TAG_FOREIGNOBJECT, GUMBO_TAG_DESC, // SVG title tags will have GUMBO_TAG_TITLE as with HTML. // http://www.whatwg.org/specs/web-apps/current-work/multipage/tabular-data.html#tabular-data GUMBO_TAG_TABLE, GUMBO_TAG_CAPTION, GUMBO_TAG_COLGROUP, GUMBO_TAG_COL, GUMBO_TAG_TBODY, GUMBO_TAG_THEAD, GUMBO_TAG_TFOOT, GUMBO_TAG_TR, GUMBO_TAG_TD, GUMBO_TAG_TH, // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#forms GUMBO_TAG_FORM, GUMBO_TAG_FIELDSET, GUMBO_TAG_LEGEND, GUMBO_TAG_LABEL, GUMBO_TAG_INPUT, GUMBO_TAG_BUTTON, GUMBO_TAG_SELECT, GUMBO_TAG_DATALIST, GUMBO_TAG_OPTGROUP, GUMBO_TAG_OPTION, GUMBO_TAG_TEXTAREA, GUMBO_TAG_KEYGEN, GUMBO_TAG_OUTPUT, GUMBO_TAG_PROGRESS, GUMBO_TAG_METER, // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#interactive-elements GUMBO_TAG_DETAILS, GUMBO_TAG_SUMMARY, GUMBO_TAG_COMMAND, GUMBO_TAG_MENU, // Non-conforming elements that nonetheless appear in the HTML5 spec. // http://www.whatwg.org/specs/web-apps/current-work/multipage/obsolete.html#non-conforming-features GUMBO_TAG_APPLET, GUMBO_TAG_ACRONYM, GUMBO_TAG_BGSOUND, GUMBO_TAG_DIR, GUMBO_TAG_FRAME, GUMBO_TAG_FRAMESET, GUMBO_TAG_NOFRAMES, GUMBO_TAG_ISINDEX, GUMBO_TAG_LISTING, GUMBO_TAG_XMP, GUMBO_TAG_NEXTID, GUMBO_TAG_NOEMBED, GUMBO_TAG_PLAINTEXT, GUMBO_TAG_RB, GUMBO_TAG_STRIKE, GUMBO_TAG_BASEFONT, GUMBO_TAG_BIG, GUMBO_TAG_BLINK, GUMBO_TAG_CENTER, GUMBO_TAG_FONT, GUMBO_TAG_MARQUEE, GUMBO_TAG_MULTICOL, GUMBO_TAG_NOBR, GUMBO_TAG_SPACER, GUMBO_TAG_TT, GUMBO_TAG_U, // Used for all tags that don't have special handling in HTML. GUMBO_TAG_UNKNOWN, // A marker value to indicate the end of the enum, for iterating over it. // Also used as the terminator for varargs functions that take tags. GUMBO_TAG_LAST, } GumboTag; /** * Returns the normalized (usually all-lowercased, except for foreign content) * tag name for an GumboTag enum. Return value is static data owned by the * library. */ const char* gumbo_normalized_tagname(GumboTag tag); /** * Extracts the tag name from the original_text field of an element or token by * stripping off characters and attributes and adjusting the passed-in * GumboStringPiece appropriately. The tag name is in the original case and * shares a buffer with the original text, to simplify memory management. * Behavior is undefined if a string-piece that doesn't represent an HTML tag * ( or ) is passed in. If the string piece is completely * empty (NULL data pointer), then this function will exit successfully as a * no-op. */ void gumbo_tag_from_original_text(GumboStringPiece* text); /** * Fixes the case of SVG elements that are not all lowercase. * http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inforeign * This is not done at parse time because there's no place to store a mutated * tag name. tag_name is an enum (which will be TAG_UNKNOWN for most SVG tags * without special handling), while original_tag_name is a pointer into the * original buffer. Instead, we provide this helper function that clients can * use to rename SVG tags as appropriate. * Returns the case-normalized SVG tagname if a replacement is found, or NULL if * no normalization is called for. The return value is static data and owned by * the library. */ const char* gumbo_normalize_svg_tagname(const GumboStringPiece* tagname); /** * Converts a tag name string (which may be in upper or mixed case) to a tag * enum. */ GumboTag gumbo_tag_enum(const char* tagname); /** * Attribute namespaces. * HTML includes special handling for XLink, XML, and XMLNS namespaces on * attributes. Everything else goes in the generatic "NONE" namespace. */ typedef enum { GUMBO_ATTR_NAMESPACE_NONE, GUMBO_ATTR_NAMESPACE_XLINK, GUMBO_ATTR_NAMESPACE_XML, GUMBO_ATTR_NAMESPACE_XMLNS, } GumboAttributeNamespaceEnum; /** * A struct representing a single attribute on an HTML tag. This is a * name-value pair, but also includes information about source locations and * original source text. */ typedef struct { /** * The namespace for the attribute. This will usually be * GUMBO_ATTR_NAMESPACE_NONE, but some XLink/XMLNS/XML attributes take special * values, per: * http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adjust-foreign-attributes */ GumboAttributeNamespaceEnum attr_namespace; /** * The name of the attribute. This is in a freshly-allocated buffer to deal * with case-normalization, and is null-terminated. */ const char* name; /** * The original text of the attribute name, as a pointer into the original * source buffer. */ GumboStringPiece original_name; /** * The value of the attribute. This is in a freshly-allocated buffer to deal * with unescaping, and is null-terminated. It does not include any quotes * that surround the attribute. If the attribute has no value (for example, * 'selected' on a checkbox), this will be an empty string. */ const char* value; /** * The original text of the value of the attribute. This points into the * original source buffer. It includes any quotes that surround the * attribute, and you can look at original_value.data[0] and * original_value.data[original_value.length - 1] to determine what the quote * characters were. If the attribute has no value, this will be a 0-length * string. */ GumboStringPiece original_value; /** The starting position of the attribute name. */ GumboSourcePosition name_start; /** * The ending position of the attribute name. This is not always derivable * from the starting position of the value because of the possibility of * whitespace around the = sign. */ GumboSourcePosition name_end; /** The starting position of the attribute value. */ GumboSourcePosition value_start; /** The ending position of the attribute value. */ GumboSourcePosition value_end; } GumboAttribute; /** * Given a vector of GumboAttributes, look up the one with the specified name * and return it, or NULL if no such attribute exists. This uses a * case-insensitive match, as HTML is case-insensitive. */ GumboAttribute* gumbo_get_attribute(const GumboVector* attrs, const char* name); /** * Enum denoting the type of node. This determines the type of the node.v * union. */ typedef enum { /** Document node. v will be a GumboDocument. */ GUMBO_NODE_DOCUMENT, /** Element node. v will be a GumboElement. */ GUMBO_NODE_ELEMENT, /** Text node. v will be a GumboText. */ GUMBO_NODE_TEXT, /** CDATA node. v will be a GumboText. */ GUMBO_NODE_CDATA, /** Comment node. v. will be a GumboText, excluding comment delimiters. */ GUMBO_NODE_COMMENT, /** Text node, where all contents is whitespace. v will be a GumboText. */ GUMBO_NODE_WHITESPACE } GumboNodeType; /** * Forward declaration of GumboNode so it can be used recursively in * GumboNode.parent. */ typedef struct GumboInternalNode GumboNode; /** http://www.whatwg.org/specs/web-apps/current-work/complete/dom.html#quirks-mode */ typedef enum { GUMBO_DOCTYPE_NO_QUIRKS, GUMBO_DOCTYPE_QUIRKS, GUMBO_DOCTYPE_LIMITED_QUIRKS } GumboQuirksModeEnum; /** * Namespaces. * Unlike in X(HT)ML, namespaces in HTML5 are not denoted by a prefix. Rather, * anything inside an tag is in the SVG namespace, anything inside the * tag is in the MathML namespace, and anything else is inside the HTML * namespace. No other namespaces are supported, so this can be an enum only. */ typedef enum { GUMBO_NAMESPACE_HTML, GUMBO_NAMESPACE_SVG, GUMBO_NAMESPACE_MATHML } GumboNamespaceEnum; /** * Parse flags. * We track the reasons for parser insertion of nodes and store them in a * bitvector in the node itself. This lets client code optimize out nodes that * are implied by the HTML structure of the document, or flag constructs that * may not be allowed by a style guide, or track the prevalence of incorrect or * tricky HTML code. */ typedef enum { /** * A normal node - both start and end tags appear in the source, nothing has * been reparented. */ GUMBO_INSERTION_NORMAL = 0, /** * A node inserted by the parser to fulfill some implicit insertion rule. * This is usually set in addition to some other flag giving a more specific * insertion reason; it's a generic catch-all term meaning "The start tag for * this node did not appear in the document source". */ GUMBO_INSERTION_BY_PARSER = 1 << 0, /** * A flag indicating that the end tag for this node did not appear in the * document source. Note that in some cases, you can still have * parser-inserted nodes with an explicit end tag: for example, "Text" * has GUMBO_INSERTED_BY_PARSER set on the node, but * GUMBO_INSERTED_END_TAG_IMPLICITLY is unset, as the tag actually * exists. This flag will be set only if the end tag is completely missing; * in some cases, the end tag may be misplaced (eg. a tag with text * afterwards), which will leave this flag unset and require clients to * inspect the parse errors for that case. */ GUMBO_INSERTION_IMPLICIT_END_TAG = 1 << 1, // Value 1 << 2 was for a flag that has since been removed. /** * A flag for nodes that are inserted because their presence is implied by * other tags, eg. , , , , etc. */ GUMBO_INSERTION_IMPLIED = 1 << 3, /** * A flag for nodes that are converted from their end tag equivalents. For * example,

when no paragraph is open implies that the parser should * create a

tag and immediately close it, while
means the same thing * as
. */ GUMBO_INSERTION_CONVERTED_FROM_END_TAG = 1 << 4, /** A flag for nodes that are converted from the parse of an tag. */ GUMBO_INSERTION_FROM_ISINDEX = 1 << 5, /** A flag for tags that are rewritten as . */ GUMBO_INSERTION_FROM_IMAGE = 1 << 6, /** * A flag for nodes that are cloned as a result of the reconstruction of * active formatting elements. This is set only on the clone; the initial * portion of the formatting run is a NORMAL node with an IMPLICIT_END_TAG. */ GUMBO_INSERTION_RECONSTRUCTED_FORMATTING_ELEMENT = 1 << 7, /** A flag for nodes that are cloned by the adoption agency algorithm. */ GUMBO_INSERTION_ADOPTION_AGENCY_CLONED = 1 << 8, /** A flag for nodes that are moved by the adoption agency algorithm. */ GUMBO_INSERTION_ADOPTION_AGENCY_MOVED = 1 << 9, /** * A flag for nodes that have been foster-parented out of a table (or * should've been foster-parented, if verbatim mode is set). */ GUMBO_INSERTION_FOSTER_PARENTED = 1 << 10, } GumboParseFlags; /** * Information specific to document nodes. */ typedef struct { /** * An array of GumboNodes, containing the children of this element. This will * normally consist of the element and any comment nodes found. * Pointers are owned. */ GumboVector /* GumboNode* */ children; // True if there was an explicit doctype token as opposed to it being omitted. bool has_doctype; // Fields from the doctype token, copied verbatim. const char* name; const char* public_identifier; const char* system_identifier; /** * Whether or not the document is in QuirksMode, as determined by the values * in the GumboTokenDocType template. */ GumboQuirksModeEnum doc_type_quirks_mode; } GumboDocument; /** * The struct used to represent TEXT, CDATA, COMMENT, and WHITESPACE elements. * This contains just a block of text and its position. */ typedef struct { /** * The text of this node, after entities have been parsed and decoded. For * comment/cdata nodes, this does not include the comment delimiters. */ const char* text; /** * The original text of this node, as a pointer into the original buffer. For * comment/cdata nodes, this includes the comment delimiters. */ GumboStringPiece original_text; /** * The starting position of this node. This corresponds to the position of * original_text, before entities are decoded. * */ GumboSourcePosition start_pos; } GumboText; /** * The struct used to represent all HTML elements. This contains information * about the tag, attributes, and child nodes. */ typedef struct { /** * An array of GumboNodes, containing the children of this element. Pointers * are owned. */ GumboVector /* GumboNode* */ children; /** The GumboTag enum for this element. */ GumboTag tag; /** The GumboNamespaceEnum for this element. */ GumboNamespaceEnum tag_namespace; /** * A GumboStringPiece pointing to the original tag text for this element, * pointing directly into the source buffer. If the tag was inserted * algorithmically (for example, or insertion), this will be a * zero-length string. */ GumboStringPiece original_tag; /** * A GumboStringPiece pointing to the original end tag text for this element. * If the end tag was inserted algorithmically, (for example, closing a * self-closing tag), this will be a zero-length string. */ GumboStringPiece original_end_tag; /** The source position for the start of the start tag. */ GumboSourcePosition start_pos; /** The source position for the start of the end tag. */ GumboSourcePosition end_pos; /** * An array of GumboAttributes, containing the attributes for this tag in the * order that they were parsed. Pointers are owned. */ GumboVector /* GumboAttribute* */ attributes; } GumboElement; /** * A supertype for GumboElement and GumboText, so that we can include one * generic type in lists of children and cast as necessary to subtypes. */ struct GumboInternalNode { /** The type of node that this is. */ GumboNodeType type; /** Pointer back to parent node. Not owned. */ GumboNode* parent; /** The index within the parent's children vector of this node. */ size_t index_within_parent; /** * A bitvector of flags containing information about why this element was * inserted into the parse tree, including a variety of special parse * situations. */ GumboParseFlags parse_flags; /** The actual node data. */ union { GumboDocument document; // For GUMBO_NODE_DOCUMENT. GumboElement element; // For GUMBO_NODE_ELEMENT. GumboText text; // For everything else. } v; }; /** * The type for an allocator function. Takes the 'userdata' member of the * GumboParser struct as its first argument. Semantics should be the same as * malloc, i.e. return a block of size_t bytes on success or NULL on failure. * Allocating a block of 0 bytes behaves as per malloc. */ // TODO(jdtang): Add checks throughout the codebase for out-of-memory condition. typedef void* (*GumboAllocatorFunction)(void* userdata, size_t size); /** * The type for a deallocator function. Takes the 'userdata' member of the * GumboParser struct as its first argument. */ typedef void (*GumboDeallocatorFunction)(void* userdata, void* ptr); /** * Input struct containing configuration options for the parser. * These let you specify alternate memory managers, provide different error * handling, etc. * Use kGumboDefaultOptions for sensible defaults, and only set what you need. */ typedef struct GumboInternalOptions { /** A memory allocator function. Default: malloc. */ GumboAllocatorFunction allocator; /** A memory deallocator function. Default: free. */ GumboDeallocatorFunction deallocator; /** * An opaque object that's passed in as the first argument to all callbacks * used by this library. Default: NULL. */ void* userdata; /** * The tab-stop size, for computing positions in source code that uses tabs. * Default: 8. */ int tab_stop; /** * Whether or not to stop parsing when the first error is encountered. * Default: false. */ bool stop_on_first_error; /** * The maximum number of errors before the parser stops recording them. This * is provided so that if the page is totally borked, we don't completely fill * up the errors vector and exhaust memory with useless redundant errors. Set * to -1 to disable the limit. * Default: -1 */ int max_errors; } GumboOptions; /** Default options struct; use this with gumbo_parse_with_options. */ extern const GumboOptions kGumboDefaultOptions; /** The output struct containing the results of the parse. */ typedef struct GumboInternalOutput { /** * Pointer to the document node. This is a GumboNode of type NODE_DOCUMENT * that contains the entire document as its child. */ GumboNode* document; /** * Pointer to the root node. This the tag that forms the root of the * document. */ GumboNode* root; /** * A list of errors that occurred during the parse. * NOTE: In version 1.0 of this library, the API for errors hasn't been fully * fleshed out and may change in the future. For this reason, the GumboError * header isn't part of the public API. Contact us if you need errors * reported so we can work out something appropriate for your use-case. */ GumboVector /* GumboError */ errors; } GumboOutput; /** * Parses a buffer of UTF8 text into an GumboNode parse tree. The buffer must * live at least as long as the parse tree, as some fields (eg. original_text) * point directly into the original buffer. * * This doesn't support buffers longer than 4 gigabytes. */ GumboOutput* gumbo_parse(const char* buffer); /** * Extended version of gumbo_parse that takes an explicit options structure, * buffer, and length. */ GumboOutput* gumbo_parse_with_options( const GumboOptions* options, const char* buffer, size_t buffer_length); /** Release the memory used for the parse tree & parse errors. */ void gumbo_destroy_output( const GumboOptions* options, GumboOutput* output); #ifdef __cplusplus } #endif #endif // GUMBO_GUMBO_H_ ================================================ FILE: Pod/Classes/Private/Gumbo/insertion_mode.h ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #ifndef GUMBO_INSERTION_MODE_H_ #define GUMBO_INSERTION_MODE_H_ #ifdef __cplusplus extern "C" { #endif // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#insertion-mode typedef enum { GUMBO_INSERTION_MODE_INITIAL, GUMBO_INSERTION_MODE_BEFORE_HTML, GUMBO_INSERTION_MODE_BEFORE_HEAD, GUMBO_INSERTION_MODE_IN_HEAD, GUMBO_INSERTION_MODE_IN_HEAD_NOSCRIPT, GUMBO_INSERTION_MODE_AFTER_HEAD, GUMBO_INSERTION_MODE_IN_BODY, GUMBO_INSERTION_MODE_TEXT, GUMBO_INSERTION_MODE_IN_TABLE, GUMBO_INSERTION_MODE_IN_TABLE_TEXT, GUMBO_INSERTION_MODE_IN_CAPTION, GUMBO_INSERTION_MODE_IN_COLUMN_GROUP, GUMBO_INSERTION_MODE_IN_TABLE_BODY, GUMBO_INSERTION_MODE_IN_ROW, GUMBO_INSERTION_MODE_IN_CELL, GUMBO_INSERTION_MODE_IN_SELECT, GUMBO_INSERTION_MODE_IN_SELECT_IN_TABLE, GUMBO_INSERTION_MODE_AFTER_BODY, GUMBO_INSERTION_MODE_IN_FRAMESET, GUMBO_INSERTION_MODE_AFTER_FRAMESET, GUMBO_INSERTION_MODE_AFTER_AFTER_BODY, GUMBO_INSERTION_MODE_AFTER_AFTER_FRAMESET } GumboInsertionMode; #ifdef __cplusplus } // extern C #endif #endif // GUMBO_INSERTION_MODE_H_ ================================================ FILE: Pod/Classes/Private/Gumbo/parser.c ================================================ // Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: jdtang@google.com (Jonathan Tang) #include #include #include #include #include #include #include "attribute.h" #include "error.h" #include "gumbo.h" #include "insertion_mode.h" #include "parser.h" #include "tokenizer.h" #include "tokenizer_states.h" #include "utf8.h" #include "util.h" #include "vector.h" #define AVOID_UNUSED_VARIABLE_WARNING(i) (void)(i) #define GUMBO_STRING(literal) { literal, sizeof(literal) - 1 } #define TERMINATOR { "", 0 } static void* malloc_wrapper(void* unused, size_t size) { return malloc(size); } static void free_wrapper(void* unused, void* ptr) { return free(ptr); } const GumboOptions kGumboDefaultOptions = { &malloc_wrapper, &free_wrapper, NULL, 8, false, -1, }; static const GumboStringPiece kDoctypeHtml = GUMBO_STRING("html"); static const GumboStringPiece kPublicIdHtml4_0 = GUMBO_STRING( "-//W3C//DTD HTML 4.0//EN"); static const GumboStringPiece kPublicIdHtml4_01 = GUMBO_STRING( "-//W3C//DTD HTML 4.01//EN"); static const GumboStringPiece kPublicIdXhtml1_0 = GUMBO_STRING( "-//W3C//DTD XHTML 1.0 Strict//EN"); static const GumboStringPiece kPublicIdXhtml1_1 = GUMBO_STRING( "-//W3C//DTD XHTML 1.1//EN"); static const GumboStringPiece kSystemIdRecHtml4_0 = GUMBO_STRING( "http://www.w3.org/TR/REC-html40/strict.dtd"); static const GumboStringPiece kSystemIdHtml4 = GUMBO_STRING( "http://www.w3.org/TR/html4/strict.dtd"); static const GumboStringPiece kSystemIdXhtmlStrict1_1 = GUMBO_STRING( "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"); static const GumboStringPiece kSystemIdXhtml1_1 = GUMBO_STRING( "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"); static const GumboStringPiece kSystemIdLegacyCompat = GUMBO_STRING( "about:legacy-compat"); // The doctype arrays have an explicit terminator because we want to pass them // to a helper function, and passing them as a pointer discards sizeof // information. The SVG arrays are used only by one-off functions, and so loops // over them use sizeof directly instead of a terminator. static const GumboStringPiece kQuirksModePublicIdPrefixes[] = { GUMBO_STRING("+//Silmaril//dtd html Pro v0r11 19970101//"), GUMBO_STRING("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//"), GUMBO_STRING("-//AS//DTD HTML 3.0 asWedit + extensions//"), GUMBO_STRING("-//IETF//DTD HTML 2.0 Level 1//"), GUMBO_STRING("-//IETF//DTD HTML 2.0 Level 2//"), GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict Level 1//"), GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict Level 2//"), GUMBO_STRING("-//IETF//DTD HTML 2.0 Strict//"), GUMBO_STRING("-//IETF//DTD HTML 2.0//"), GUMBO_STRING("-//IETF//DTD HTML 2.1E//"), GUMBO_STRING("-//IETF//DTD HTML 3.0//"), GUMBO_STRING("-//IETF//DTD HTML 3.2 Final//"), GUMBO_STRING("-//IETF//DTD HTML 3.2//"), GUMBO_STRING("-//IETF//DTD HTML 3//"), GUMBO_STRING("-//IETF//DTD HTML Level 0//"), GUMBO_STRING("-//IETF//DTD HTML Level 1//"), GUMBO_STRING("-//IETF//DTD HTML Level 2//"), GUMBO_STRING("-//IETF//DTD HTML Level 3//"), GUMBO_STRING("-//IETF//DTD HTML Strict Level 0//"), GUMBO_STRING("-//IETF//DTD HTML Strict Level 1//"), GUMBO_STRING("-//IETF//DTD HTML Strict Level 2//"), GUMBO_STRING("-//IETF//DTD HTML Strict Level 3//"), GUMBO_STRING("-//IETF//DTD HTML Strict//"), GUMBO_STRING("-//IETF//DTD HTML//"), GUMBO_STRING("-//Metrius//DTD Metrius Presentational//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 HTML//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 2.0 Tables//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 HTML//"), GUMBO_STRING("-//Microsoft//DTD Internet Explorer 3.0 Tables//"), GUMBO_STRING("-//Netscape Comm. Corp.//DTD HTML//"), GUMBO_STRING("-//Netscape Comm. Corp.//DTD Strict HTML//"), GUMBO_STRING("-//O'Reilly and Associates//DTD HTML 2.0//"), GUMBO_STRING("-//O'Reilly and Associates//DTD HTML Extended 1.0//"), GUMBO_STRING("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//"), GUMBO_STRING("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::)" "extensions to HTML 4.0//"), GUMBO_STRING("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::" "extensions to HTML 4.0//"), GUMBO_STRING("-//Spyglass//DTD HTML 2.0 Extended//"), GUMBO_STRING("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//"), GUMBO_STRING("-//Sun Microsystems Corp.//DTD HotJava HTML//"), GUMBO_STRING("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//"), GUMBO_STRING("-//W3C//DTD HTML 3 1995-03-24//"), GUMBO_STRING("-//W3C//DTD HTML 3.2 Draft//"), GUMBO_STRING("-//W3C//DTD HTML 3.2 Final//"), GUMBO_STRING("-//W3C//DTD HTML 3.2//"), GUMBO_STRING("-//W3C//DTD HTML 3.2S Draft//"), GUMBO_STRING("-//W3C//DTD HTML 4.0 Frameset//"), GUMBO_STRING("-//W3C//DTD HTML 4.0 Transitional//"), GUMBO_STRING("-//W3C//DTD HTML Experimental 19960712//"), GUMBO_STRING("-//W3C//DTD HTML Experimental 970421//"), GUMBO_STRING("-//W3C//DTD W3 HTML//"), GUMBO_STRING("-//W3O//DTD W3 HTML 3.0//"), GUMBO_STRING("-//WebTechs//DTD Mozilla HTML 2.0//"), GUMBO_STRING("-//WebTechs//DTD Mozilla HTML//"), TERMINATOR }; static const GumboStringPiece kQuirksModePublicIdExactMatches[] = { GUMBO_STRING("-//W3O//DTD W3 HTML Strict 3.0//EN//"), GUMBO_STRING("-/W3C/DTD HTML 4.0 Transitional/EN"), GUMBO_STRING("HTML"), TERMINATOR }; static const GumboStringPiece kQuirksModeSystemIdExactMatches[] = { GUMBO_STRING("http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"), TERMINATOR }; static const GumboStringPiece kLimitedQuirksPublicIdPrefixes[] = { GUMBO_STRING("-//W3C//DTD XHTML 1.0 Frameset//"), GUMBO_STRING("-//W3C//DTD XHTML 1.0 Transitional//"), TERMINATOR }; static const GumboStringPiece kLimitedQuirksRequiresSystemIdPublicIdPrefixes[] = { GUMBO_STRING("-//W3C//DTD HTML 4.01 Frameset//"), GUMBO_STRING("-//W3C//DTD HTML 4.01 Transitional//"), TERMINATOR }; // Indexed by GumboNamespaceEnum; keep in sync with that. static const char* kLegalXmlns[] = { "http://www.w3.org/1999/xhtml", "http://www.w3.org/2000/svg", "http://www.w3.org/1998/Math/MathML" }; typedef struct _ReplacementEntry { const GumboStringPiece from; const GumboStringPiece to; } ReplacementEntry; #define REPLACEMENT_ENTRY(from, to) \ { GUMBO_STRING(from), GUMBO_STRING(to) } // Static data for SVG attribute replacements. // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adjust-svg-attributes static const ReplacementEntry kSvgAttributeReplacements[] = { REPLACEMENT_ENTRY("attributename", "attributeName"), REPLACEMENT_ENTRY("attributetype", "attributeType"), REPLACEMENT_ENTRY("basefrequency", "baseFrequency"), REPLACEMENT_ENTRY("baseprofile", "baseProfile"), REPLACEMENT_ENTRY("calcmode", "calcMode"), REPLACEMENT_ENTRY("clippathunits", "clipPathUnits"), REPLACEMENT_ENTRY("contentscripttype", "contentScriptType"), REPLACEMENT_ENTRY("contentstyletype", "contentStyleType"), REPLACEMENT_ENTRY("diffuseconstant", "diffuseConstant"), REPLACEMENT_ENTRY("edgemode", "edgeMode"), REPLACEMENT_ENTRY("externalresourcesrequired", "externalResourcesRequired"), REPLACEMENT_ENTRY("filterres", "filterRes"), REPLACEMENT_ENTRY("filterunits", "filterUnits"), REPLACEMENT_ENTRY("glyphref", "glyphRef"), REPLACEMENT_ENTRY("gradienttransform", "gradientTransform"), REPLACEMENT_ENTRY("gradientunits", "gradientUnits"), REPLACEMENT_ENTRY("kernelmatrix", "kernelMatrix"), REPLACEMENT_ENTRY("kernelunitlength", "kernelUnitLength"), REPLACEMENT_ENTRY("keypoints", "keyPoints"), REPLACEMENT_ENTRY("keysplines", "keySplines"), REPLACEMENT_ENTRY("keytimes", "keyTimes"), REPLACEMENT_ENTRY("lengthadjust", "lengthAdjust"), REPLACEMENT_ENTRY("limitingconeangle", "limitingConeAngle"), REPLACEMENT_ENTRY("markerheight", "markerHeight"), REPLACEMENT_ENTRY("markerunits", "markerUnits"), REPLACEMENT_ENTRY("markerwidth", "markerWidth"), REPLACEMENT_ENTRY("maskcontentunits", "maskContentUnits"), REPLACEMENT_ENTRY("maskunits", "maskUnits"), REPLACEMENT_ENTRY("numoctaves", "numOctaves"), REPLACEMENT_ENTRY("pathlength", "pathLength"), REPLACEMENT_ENTRY("patterncontentunits", "patternContentUnits"), REPLACEMENT_ENTRY("patterntransform", "patternTransform"), REPLACEMENT_ENTRY("patternunits", "patternUnits"), REPLACEMENT_ENTRY("pointsatx", "pointsAtX"), REPLACEMENT_ENTRY("pointsaty", "pointsAtY"), REPLACEMENT_ENTRY("pointsatz", "pointsAtZ"), REPLACEMENT_ENTRY("preservealpha", "preserveAlpha"), REPLACEMENT_ENTRY("preserveaspectratio", "preserveAspectRatio"), REPLACEMENT_ENTRY("primitiveunits", "primitiveUnits"), REPLACEMENT_ENTRY("refx", "refX"), REPLACEMENT_ENTRY("refy", "refY"), REPLACEMENT_ENTRY("repeatcount", "repeatCount"), REPLACEMENT_ENTRY("repeatdur", "repeatDur"), REPLACEMENT_ENTRY("requiredextensions", "requiredExtensions"), REPLACEMENT_ENTRY("requiredfeatures", "requiredFeatures"), REPLACEMENT_ENTRY("specularconstant", "specularConstant"), REPLACEMENT_ENTRY("specularexponent", "specularExponent"), REPLACEMENT_ENTRY("spreadmethod", "spreadMethod"), REPLACEMENT_ENTRY("startoffset", "startOffset"), REPLACEMENT_ENTRY("stddeviation", "stdDeviation"), REPLACEMENT_ENTRY("stitchtiles", "stitchTiles"), REPLACEMENT_ENTRY("surfacescale", "surfaceScale"), REPLACEMENT_ENTRY("systemlanguage", "systemLanguage"), REPLACEMENT_ENTRY("tablevalues", "tableValues"), REPLACEMENT_ENTRY("targetx", "targetX"), REPLACEMENT_ENTRY("targety", "targetY"), REPLACEMENT_ENTRY("textlength", "textLength"), REPLACEMENT_ENTRY("viewbox", "viewBox"), REPLACEMENT_ENTRY("viewtarget", "viewTarget"), REPLACEMENT_ENTRY("xchannelselector", "xChannelSelector"), REPLACEMENT_ENTRY("ychannelselector", "yChannelSelector"), REPLACEMENT_ENTRY("zoomandpan", "zoomAndPan"), }; static const ReplacementEntry kSvgTagReplacements[] = { REPLACEMENT_ENTRY("altglyph", "altGlyph"), REPLACEMENT_ENTRY("altglyphdef", "altGlyphDef"), REPLACEMENT_ENTRY("altglyphitem", "altGlyphItem"), REPLACEMENT_ENTRY("animatecolor", "animateColor"), REPLACEMENT_ENTRY("animatemotion", "animateMotion"), REPLACEMENT_ENTRY("animatetransform", "animateTransform"), REPLACEMENT_ENTRY("clippath", "clipPath"), REPLACEMENT_ENTRY("feblend", "feBlend"), REPLACEMENT_ENTRY("fecolormatrix", "feColorMatrix"), REPLACEMENT_ENTRY("fecomponenttransfer", "feComponentTransfer"), REPLACEMENT_ENTRY("fecomposite", "feComposite"), REPLACEMENT_ENTRY("feconvolvematrix", "feConvolveMatrix"), REPLACEMENT_ENTRY("fediffuselighting", "feDiffuseLighting"), REPLACEMENT_ENTRY("fedisplacementmap", "feDisplacementMap"), REPLACEMENT_ENTRY("fedistantlight", "feDistantLight"), REPLACEMENT_ENTRY("feflood", "feFlood"), REPLACEMENT_ENTRY("fefunca", "feFuncA"), REPLACEMENT_ENTRY("fefuncb", "feFuncB"), REPLACEMENT_ENTRY("fefuncg", "feFuncG"), REPLACEMENT_ENTRY("fefuncr", "feFuncR"), REPLACEMENT_ENTRY("fegaussianblur", "feGaussianBlur"), REPLACEMENT_ENTRY("feimage", "feImage"), REPLACEMENT_ENTRY("femerge", "feMerge"), REPLACEMENT_ENTRY("femergenode", "feMergeNode"), REPLACEMENT_ENTRY("femorphology", "feMorphology"), REPLACEMENT_ENTRY("feoffset", "feOffset"), REPLACEMENT_ENTRY("fepointlight", "fePointLight"), REPLACEMENT_ENTRY("fespecularlighting", "feSpecularLighting"), REPLACEMENT_ENTRY("fespotlight", "feSpotLight"), REPLACEMENT_ENTRY("fetile", "feTile"), REPLACEMENT_ENTRY("feturbulence", "feTurbulence"), REPLACEMENT_ENTRY("foreignobject", "foreignObject"), REPLACEMENT_ENTRY("glyphref", "glyphRef"), REPLACEMENT_ENTRY("lineargradient", "linearGradient"), REPLACEMENT_ENTRY("radialgradient", "radialGradient"), REPLACEMENT_ENTRY("textpath", "textPath"), }; typedef struct _NamespacedAttributeReplacement { const char* from; const char* local_name; const GumboAttributeNamespaceEnum attr_namespace; } NamespacedAttributeReplacement; static const NamespacedAttributeReplacement kForeignAttributeReplacements[] = { { "xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK }, { "xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK }, { "xml:base", "base", GUMBO_ATTR_NAMESPACE_XML }, { "xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML }, { "xml:space", "space", GUMBO_ATTR_NAMESPACE_XML }, { "xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS }, { "xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS }, }; // The "scope marker" for the list of active formatting elements. We use a // pointer to this as a generic marker element, since the particular element // scope doesn't matter. static const GumboNode kActiveFormattingScopeMarker; // The tag_is and tag_in function use true & false to denote start & end tags, // but for readability, we define constants for them here. static const bool kStartTag = true; static const bool kEndTag = false; // Because GumboStringPieces are immutable, we can't insert a character directly // into a text node. Instead, we accumulate all pending characters here and // flush them out to a text node whenever a new element is inserted. // // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#insert-a-character typedef struct _TextNodeBufferState { // The accumulated text to be inserted into the current text node. GumboStringBuffer _buffer; // A pointer to the original text represented by this text node. Note that // because of foster parenting and other strange DOM manipulations, this may // include other non-text HTML tags in it; it is defined as the span of // original text from the first character in this text node to the last // character in this text node. const char* _start_original_text; // The source position of the start of this text node. GumboSourcePosition _start_position; // The type of node that will be inserted (TEXT or WHITESPACE). GumboNodeType _type; } TextNodeBufferState; typedef struct GumboInternalParserState { // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#insertion-mode GumboInsertionMode _insertion_mode; // Used for run_generic_parsing_algorithm, which needs to switch back to the // original insertion mode at its conclusion. GumboInsertionMode _original_insertion_mode; // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-stack-of-open-elements GumboVector /*GumboNode*/ _open_elements; // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-list-of-active-formatting-elements GumboVector /*GumboNode*/ _active_formatting_elements; // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#the-element-pointers GumboNode* _head_element; GumboNode* _form_element; // The flag for when the spec says "Reprocess the current token in..." bool _reprocess_current_token; // The flag for "acknowledge the token's self-closing flag". bool _self_closing_flag_acknowledged; // The "frameset-ok" flag from the spec. bool _frameset_ok; // The flag for "If the next token is a LINE FEED, ignore that token...". bool _ignore_next_linefeed; // The flag for "whenever a node would be inserted into the current node, it // must instead be foster parented". This is used for misnested table // content, which needs to be handled according to "in body" rules yet foster // parented outside of the table. // It would perhaps be more explicit to have this as a parameter to // handle_in_body and insert_element, but given how special-purpose this is // and the number of call-sites that would need to take the extra parameter, // it's easier just to have a state flag. bool _foster_parent_insertions; // The accumulated text node buffer state. TextNodeBufferState _text_node; // The current token. GumboToken* _current_token; // The way that the spec is written, the and tags are *always* // implicit, because encountering one of those tokens merely switches the // insertion mode out of "in body". So we have individual state flags for // those end tags that are then inspected by pop_current_node when the // and nodes are popped to set the GUMBO_INSERTION_IMPLICIT_END_TAG // flag appropriately. bool _closed_body_tag; bool _closed_html_tag; } GumboParserState; static bool token_has_attribute(const GumboToken* token, const char* name) { assert(token->type == GUMBO_TOKEN_START_TAG); return gumbo_get_attribute(&token->v.start_tag.attributes, name) != NULL; } // Checks if the value of the specified attribute is a case-insensitive match // for the specified string. static bool attribute_matches( const GumboVector* attributes, const char* name, const char* value) { const GumboAttribute* attr = gumbo_get_attribute(attributes, name); return attr ? strcasecmp(value, attr->value) == 0 : false; } // Checks if the value of the specified attribute is a case-sensitive match // for the specified string. static bool attribute_matches_case_sensitive( const GumboVector* attributes, const char* name, const char* value) { const GumboAttribute* attr = gumbo_get_attribute(attributes, name); return attr ? strcmp(value, attr->value) == 0 : false; } // Checks if the specified attribute vectors are identical. static bool all_attributes_match( const GumboVector* attr1, const GumboVector* attr2) { int num_unmatched_attr2_elements = attr2->length; for (int i = 0; i < attr1->length; ++i) { const GumboAttribute* attr = attr1->data[i]; if (attribute_matches_case_sensitive(attr2, attr->name, attr->value)) { --num_unmatched_attr2_elements; } else { return false; } } return num_unmatched_attr2_elements == 0; } static void set_frameset_not_ok(GumboParser* parser) { gumbo_debug("Setting frameset_ok to false.\n"); parser->_parser_state->_frameset_ok = false; } static GumboNode* create_node(GumboParser* parser, GumboNodeType type) { GumboNode* node = gumbo_parser_allocate(parser, sizeof(GumboNode)); node->parent = NULL; node->index_within_parent = -1; node->type = type; node->parse_flags = GUMBO_INSERTION_NORMAL; return node; } static GumboNode* new_document_node(GumboParser* parser) { GumboNode* document_node = create_node(parser, GUMBO_NODE_DOCUMENT); document_node->parse_flags = GUMBO_INSERTION_BY_PARSER; gumbo_vector_init( parser, 1, &document_node->v.document.children); // Must be initialized explicitly, as there's no guarantee that we'll see a // doc type token. GumboDocument* document = &document_node->v.document; document->has_doctype = false; document->name = NULL; document->public_identifier = NULL; document->system_identifier = NULL; return document_node; } static void output_init(GumboParser* parser) { GumboOutput* output = gumbo_parser_allocate(parser, sizeof(GumboOutput)); output->root = NULL; output->document = new_document_node(parser); parser->_output = output; gumbo_init_errors(parser); } static void parser_state_init(GumboParser* parser) { GumboParserState* parser_state = gumbo_parser_allocate(parser, sizeof(GumboParserState)); parser_state->_insertion_mode = GUMBO_INSERTION_MODE_INITIAL; parser_state->_reprocess_current_token = false; parser_state->_frameset_ok = true; parser_state->_ignore_next_linefeed = false; parser_state->_foster_parent_insertions = false; parser_state->_text_node._type = GUMBO_NODE_WHITESPACE; gumbo_string_buffer_init(parser, &parser_state->_text_node._buffer); gumbo_vector_init(parser, 10, &parser_state->_open_elements); gumbo_vector_init(parser, 5, &parser_state->_active_formatting_elements); parser_state->_head_element = NULL; parser_state->_form_element = NULL; parser_state->_current_token = NULL; parser_state->_closed_body_tag = false; parser_state->_closed_html_tag = false; parser->_parser_state = parser_state; } static void parser_state_destroy(GumboParser* parser) { GumboParserState* state = parser->_parser_state; gumbo_vector_destroy(parser, &state->_active_formatting_elements); gumbo_vector_destroy(parser, &state->_open_elements); gumbo_string_buffer_destroy(parser, &state->_text_node._buffer); gumbo_parser_deallocate(parser, state); } static GumboNode* get_document_node(GumboParser* parser) { return parser->_output->document; } // Returns the node at the bottom of the stack of open elements, or NULL if no // elements have been added yet. static GumboNode* get_current_node(GumboParser* parser) { GumboVector* open_elements = &parser->_parser_state->_open_elements; if (open_elements->length == 0) { assert(!parser->_output->root); return NULL; } assert(open_elements->length > 0); assert(open_elements->data != NULL); return open_elements->data[open_elements->length - 1]; } // Returns true if the given needle is in the given array of literal // GumboStringPieces. If exact_match is true, this requires that they match // exactly; otherwise, this performs a prefix match to check if any of the // elements in haystack start with needle. This always performs a // case-insensitive match. static bool is_in_static_list( const char* needle, const GumboStringPiece* haystack, bool exact_match) { for (int i = 0; haystack[i].length > 0; ++i) { if ((exact_match && !strcmp(needle, haystack[i].data)) || (!exact_match && !strcasecmp(needle, haystack[i].data))) { return true; } } return false; } static void set_insertion_mode(GumboParser* parser, GumboInsertionMode mode) { parser->_parser_state->_insertion_mode = mode; } // http://www.whatwg.org/specs/web-apps/current-work/complete/parsing.html#reset-the-insertion-mode-appropriately // This is a helper function that returns the appropriate insertion mode instead // of setting it. Returns GUMBO_INSERTION_MODE_INITIAL as a sentinel value to // indicate that there is no appropriate insertion mode, and the loop should // continue. static GumboInsertionMode get_appropriate_insertion_mode( const GumboNode* node, bool is_last) { assert(node->type == GUMBO_NODE_ELEMENT); switch (node->v.element.tag) { case GUMBO_TAG_SELECT: return GUMBO_INSERTION_MODE_IN_SELECT; case GUMBO_TAG_TD: case GUMBO_TAG_TH: return is_last ? GUMBO_INSERTION_MODE_IN_BODY : GUMBO_INSERTION_MODE_IN_CELL; case GUMBO_TAG_TR: return GUMBO_INSERTION_MODE_IN_ROW; case GUMBO_TAG_TBODY: case GUMBO_TAG_THEAD: case GUMBO_TAG_TFOOT: return GUMBO_INSERTION_MODE_IN_TABLE_BODY; case GUMBO_TAG_CAPTION: return GUMBO_INSERTION_MODE_IN_CAPTION; case GUMBO_TAG_COLGROUP: return GUMBO_INSERTION_MODE_IN_COLUMN_GROUP; case GUMBO_TAG_TABLE: return GUMBO_INSERTION_MODE_IN_TABLE; case GUMBO_TAG_HEAD: case GUMBO_TAG_BODY: return GUMBO_INSERTION_MODE_IN_BODY; case GUMBO_TAG_FRAMESET: return GUMBO_INSERTION_MODE_IN_FRAMESET; case GUMBO_TAG_HTML: return GUMBO_INSERTION_MODE_BEFORE_HEAD; default: return is_last ? GUMBO_INSERTION_MODE_IN_BODY : GUMBO_INSERTION_MODE_INITIAL; } } // This performs the actual "reset the insertion mode" loop. static void reset_insertion_mode_appropriately(GumboParser* parser) { const GumboVector* open_elements = &parser->_parser_state->_open_elements; for (int i = open_elements->length; --i >= 0; ) { GumboInsertionMode mode = get_appropriate_insertion_mode(open_elements->data[i], i == 0); if (mode != GUMBO_INSERTION_MODE_INITIAL) { set_insertion_mode(parser, mode); return; } } // Should never get here, because is_last will be set on the last iteration // and will force GUMBO_INSERTION_MODE_IN_BODY. assert(0); } static GumboError* add_parse_error(GumboParser* parser, const GumboToken* token) { gumbo_debug("Adding parse error.\n"); GumboError* error = gumbo_add_error(parser); if (!error) { return NULL; } error->type = GUMBO_ERR_PARSER; error->position = token->position; error->original_text = token->original_text.data; GumboParserError* extra_data = &error->v.parser; extra_data->input_type = token->type; extra_data->input_tag = GUMBO_TAG_UNKNOWN; if (token->type == GUMBO_TOKEN_START_TAG) { extra_data->input_tag = token->v.start_tag.tag; } else if (token->type == GUMBO_TOKEN_END_TAG) { extra_data->input_tag = token->v.end_tag; } GumboParserState* state = parser->_parser_state; extra_data->parser_state = state->_insertion_mode; gumbo_vector_init(parser, state->_open_elements.length, &extra_data->tag_stack); for (int i = 0; i < state->_open_elements.length; ++i) { const GumboNode* node = state->_open_elements.data[i]; assert(node->type == GUMBO_NODE_ELEMENT); gumbo_vector_add(parser, (void*) node->v.element.tag, &extra_data->tag_stack); } return error; } // Returns true if the specified token is either a start or end tag (specified // by is_start) with one of the tag types in the varargs list. Terminate the // list with GUMBO_TAG_LAST; this functions as a sentinel since no portion of // the spec references tags that are not in the spec. // TODO(jdtang): A lot of the tag lists for this function are repeated in many // places in the code. This is how it's written in the spec (and it's done this // way so it's easy to verify the code against the spec), but it may be worth // coming up with a notion of a "tag set" that includes a list of tags, and // using that in many places. It'd probably also help performance, but I want // to profile before optimizing. static bool tag_in(const GumboToken* token, bool is_start, ...) { GumboTag token_tag; if (is_start && token->type == GUMBO_TOKEN_START_TAG) { token_tag = token->v.start_tag.tag; } else if (!is_start && token->type == GUMBO_TOKEN_END_TAG) { token_tag = token->v.end_tag; } else { return false; } va_list tags; va_start(tags, is_start); bool result = false; for (GumboTag tag = va_arg(tags, GumboTag); tag != GUMBO_TAG_LAST; tag = va_arg(tags, GumboTag)) { if (tag == token_tag) { result = true; break; } } va_end(tags); return result; } // Like tag_in, but for the single-tag case. static bool tag_is(const GumboToken* token, bool is_start, GumboTag tag) { if (is_start && token->type == GUMBO_TOKEN_START_TAG) { return token->v.start_tag.tag == tag; } else if (!is_start && token->type == GUMBO_TOKEN_END_TAG) { return token->v.end_tag == tag; } else { return false; } } // Like tag_in, but checks for the tag of a node, rather than a token. static bool node_tag_in(const GumboNode* node, ...) { assert(node != NULL); if (node->type != GUMBO_NODE_ELEMENT) { return false; } GumboTag node_tag = node->v.element.tag; va_list tags; va_start(tags, node); bool result = false; for (GumboTag tag = va_arg(tags, GumboTag); tag != GUMBO_TAG_LAST; tag = va_arg(tags, GumboTag)) { assert(tag <= GUMBO_TAG_LAST); if (tag == node_tag) { result = true; break; } } va_end(tags); return result; } // Like node_tag_in, but for the single-tag case. static bool node_tag_is(const GumboNode* node, GumboTag tag) { return node->type == GUMBO_NODE_ELEMENT && node->v.element.tag == tag; } // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#mathml-text-integration-point static bool is_mathml_integration_point(const GumboNode* node) { return node_tag_in(node, GUMBO_TAG_MI, GUMBO_TAG_MO, GUMBO_TAG_MN, GUMBO_TAG_MS, GUMBO_TAG_MTEXT, GUMBO_TAG_LAST) && node->v.element.tag_namespace == GUMBO_NAMESPACE_MATHML; } // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point static bool is_html_integration_point(const GumboNode* node) { return (node_tag_in(node, GUMBO_TAG_FOREIGNOBJECT, GUMBO_TAG_DESC, GUMBO_TAG_TITLE, GUMBO_TAG_LAST) && node->v.element.tag_namespace == GUMBO_NAMESPACE_SVG) || (node_tag_is(node, GUMBO_TAG_ANNOTATION_XML) && ( attribute_matches(&node->v.element.attributes, "encoding", "text/html") || attribute_matches(&node->v.element.attributes, "encoding", "application/xhtml+xml"))); } // Appends a node to the end of its parent, setting the "parent" and // "index_within_parent" fields appropriately. static void append_node( GumboParser* parser, GumboNode* parent, GumboNode* node) { assert(node->parent == NULL); assert(node->index_within_parent = -1); GumboVector* children; if (parent->type == GUMBO_NODE_ELEMENT) { children = &parent->v.element.children; } else { assert(parent->type == GUMBO_NODE_DOCUMENT); children = &parent->v.document.children; } node->parent = parent; node->index_within_parent = children->length; gumbo_vector_add(parser, (void*) node, children); assert(node->index_within_parent < children->length); } // Inserts a node at the specified index within its parent, updating the // "parent" and "index_within_parent" fields of it and all its siblings. static void insert_node( GumboParser* parser, GumboNode* parent, int index, GumboNode* node) { assert(node->parent == NULL); assert(node->index_within_parent = -1); assert(parent->type == GUMBO_NODE_ELEMENT); GumboVector* children = &parent->v.element.children; assert(index >= 0); assert(index < children->length); node->parent = parent; node->index_within_parent = index; gumbo_vector_insert_at(parser, (void*) node, index, children); assert(node->index_within_parent < children->length); for (int i = index + 1; i < children->length; ++i) { GumboNode* sibling = children->data[i]; sibling->index_within_parent = i; assert(sibling->index_within_parent < children->length); } } // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#foster-parenting static void foster_parent_element(GumboParser* parser, GumboNode* node) { GumboVector* open_elements = &parser->_parser_state->_open_elements; assert(open_elements->length > 2); node->parse_flags |= GUMBO_INSERTION_FOSTER_PARENTED; GumboNode* foster_parent_element = open_elements->data[0]; assert(foster_parent_element->type == GUMBO_NODE_ELEMENT); assert(node_tag_is(foster_parent_element, GUMBO_TAG_HTML)); for (int i = open_elements->length; --i > 1; ) { GumboNode* table_element = open_elements->data[i]; if (node_tag_is(table_element, GUMBO_TAG_TABLE)) { foster_parent_element = table_element->parent; if (!foster_parent_element || foster_parent_element->type != GUMBO_NODE_ELEMENT) { // Table has no parent; spec says it's possible if a script manipulated // the DOM, although I don't think we have to worry about this case. gumbo_debug("Table has no parent.\n"); foster_parent_element = open_elements->data[i - 1]; break; } assert(foster_parent_element->type == GUMBO_NODE_ELEMENT); gumbo_debug("Found enclosing table (%x) at %d; parent=%s, index=%d.\n", table_element, i, gumbo_normalized_tagname( foster_parent_element->v.element.tag), table_element->index_within_parent); assert(foster_parent_element->v.element.children.data[ table_element->index_within_parent] == table_element); insert_node(parser, foster_parent_element, (int)table_element->index_within_parent, node); return; } } if (node->type == GUMBO_NODE_ELEMENT) { gumbo_vector_add(parser, (void*) node, open_elements); } append_node(parser, foster_parent_element, node); } static void maybe_flush_text_node_buffer(GumboParser* parser) { GumboParserState* state = parser->_parser_state; TextNodeBufferState* buffer_state = &state->_text_node; if (buffer_state->_buffer.length == 0) { return; } assert(buffer_state->_type == GUMBO_NODE_WHITESPACE || buffer_state->_type == GUMBO_NODE_TEXT); GumboNode* text_node = create_node(parser, buffer_state->_type); GumboText* text_node_data = &text_node->v.text; text_node_data->text = gumbo_string_buffer_to_string( parser, &buffer_state->_buffer); text_node_data->original_text.data = buffer_state->_start_original_text; text_node_data->original_text.length = state->_current_token->original_text.data - buffer_state->_start_original_text; text_node_data->start_pos = buffer_state->_start_position; if (state->_foster_parent_insertions && node_tag_in( get_current_node(parser), GUMBO_TAG_TABLE, GUMBO_TAG_TBODY, GUMBO_TAG_TFOOT, GUMBO_TAG_THEAD, GUMBO_TAG_TR, GUMBO_TAG_LAST)) { foster_parent_element(parser, text_node); } else { append_node( parser, parser->_output->root ? get_current_node(parser) : parser->_output->document, text_node); } gumbo_debug("Flushing text node buffer of %.*s.\n", (int) buffer_state->_buffer.length, buffer_state->_buffer.data); gumbo_string_buffer_destroy(parser, &buffer_state->_buffer); gumbo_string_buffer_init(parser, &buffer_state->_buffer); buffer_state->_type = GUMBO_NODE_WHITESPACE; assert(buffer_state->_buffer.length == 0); } static void record_end_of_element( GumboToken* current_token, GumboElement* element) { element->end_pos = current_token->position; element->original_end_tag = current_token->type == GUMBO_TOKEN_END_TAG ? current_token->original_text : kGumboEmptyString; } static GumboNode* pop_current_node(GumboParser* parser) { GumboParserState* state = parser->_parser_state; maybe_flush_text_node_buffer(parser); if (state->_open_elements.length > 0) { assert(node_tag_is(state->_open_elements.data[0], GUMBO_TAG_HTML)); gumbo_debug( "Popping %s node.\n", gumbo_normalized_tagname(get_current_node(parser)->v.element.tag)); } GumboNode* current_node = gumbo_vector_pop(parser, &state->_open_elements); if (!current_node) { assert(state->_open_elements.length == 0); return NULL; } assert(current_node->type == GUMBO_NODE_ELEMENT); bool is_closed_body_or_html_tag = (node_tag_is(current_node, GUMBO_TAG_BODY) && state->_closed_body_tag) || (node_tag_is(current_node, GUMBO_TAG_HTML) && state->_closed_html_tag); if ((state->_current_token->type != GUMBO_TOKEN_END_TAG || !node_tag_is(current_node, state->_current_token->v.end_tag)) && !is_closed_body_or_html_tag) { current_node->parse_flags |= GUMBO_INSERTION_IMPLICIT_END_TAG; } if (!is_closed_body_or_html_tag) { record_end_of_element(state->_current_token, ¤t_node->v.element); } return current_node; } static void append_comment_node( GumboParser* parser, GumboNode* node, const GumboToken* token) { maybe_flush_text_node_buffer(parser); GumboNode* comment = create_node(parser, GUMBO_NODE_COMMENT); comment->type = GUMBO_NODE_COMMENT; comment->parse_flags = GUMBO_INSERTION_NORMAL; comment->v.text.text = token->v.text; comment->v.text.original_text = token->original_text; comment->v.text.start_pos = token->position; append_node(parser, node, comment); } // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-row-context static void clear_stack_to_table_row_context(GumboParser* parser) { while (!node_tag_in(get_current_node(parser), GUMBO_TAG_HTML, GUMBO_TAG_TR, GUMBO_TAG_LAST)) { pop_current_node(parser); } } // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-context static void clear_stack_to_table_context(GumboParser* parser) { while (!node_tag_in(get_current_node(parser), GUMBO_TAG_HTML, GUMBO_TAG_TABLE, GUMBO_TAG_LAST)) { pop_current_node(parser); } } // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#clear-the-stack-back-to-a-table-body-context void clear_stack_to_table_body_context(GumboParser* parser) { while (!node_tag_in(get_current_node(parser), GUMBO_TAG_HTML, GUMBO_TAG_TBODY, GUMBO_TAG_TFOOT, GUMBO_TAG_THEAD, GUMBO_TAG_LAST)) { pop_current_node(parser); } } // Creates a parser-inserted element in the HTML namespace and returns it. static GumboNode* create_element(GumboParser* parser, GumboTag tag) { GumboNode* node = create_node(parser, GUMBO_NODE_ELEMENT); GumboElement* element = &node->v.element; gumbo_vector_init(parser, 1, &element->children); gumbo_vector_init(parser, 0, &element->attributes); element->tag = tag; element->tag_namespace = GUMBO_NAMESPACE_HTML; element->original_tag = kGumboEmptyString; element->original_end_tag = kGumboEmptyString; element->start_pos = parser->_parser_state->_current_token->position; element->end_pos = kGumboEmptySourcePosition; return node; } // Constructs an element from the given start tag token. static GumboNode* create_element_from_token( GumboParser* parser, GumboToken* token, GumboNamespaceEnum tag_namespace) { assert(token->type == GUMBO_TOKEN_START_TAG); GumboTokenStartTag* start_tag = &token->v.start_tag; GumboNode* node = create_node(parser, GUMBO_NODE_ELEMENT); GumboElement* element = &node->v.element; gumbo_vector_init(parser, 1, &element->children); element->attributes = start_tag->attributes; element->tag = start_tag->tag; element->tag_namespace = tag_namespace; assert(token->original_text.length >= 2); assert(token->original_text.data[0] == '<'); assert(token->original_text.data[token->original_text.length - 1] == '>'); element->original_tag = token->original_text; element->start_pos = token->position; element->original_end_tag = kGumboEmptyString; element->end_pos = kGumboEmptySourcePosition; // The element takes ownership of the attributes from the token, so any // allocated-memory fields should be nulled out. start_tag->attributes = kGumboEmptyVector; return node; } // http://www.whatwg.org/specs/web-apps/current-work/complete/tokenization.html#insert-an-html-element static void insert_element(GumboParser* parser, GumboNode* node, bool is_reconstructing_formatting_elements) { GumboParserState* state = parser->_parser_state; // NOTE(jdtang): The text node buffer must always be flushed before inserting // a node, otherwise we're handling nodes in a different order than the spec // mandated. However, one clause of the spec (character tokens in the body) // requires that we reconstruct the active formatting elements *before* adding // the character, and reconstructing the active formatting elements may itself // result in the insertion of new elements (which should be pushed onto the // stack of open elements before the buffer is flushed). We solve this (for // the time being, the spec has been rewritten for