├── Images ├── Screen1.png ├── Screen2.png ├── Screen3.png └── Screen4.png ├── .gitmodules ├── GRDBCustomSQLite ├── GRDBCustomSQLite-USER.h ├── GRDBCustomSQLite-USER.xcconfig ├── SQLiteLib-USER.xcconfig └── setup.sh ├── Pods ├── Target Support Files │ ├── Fuzi │ │ ├── Fuzi.modulemap │ │ ├── Fuzi-dummy.m │ │ ├── Fuzi-prefix.pch │ │ ├── Fuzi-umbrella.h │ │ ├── Fuzi.xcconfig │ │ └── Info.plist │ ├── GRMustache.swift │ │ ├── GRMustache.swift.modulemap │ │ ├── GRMustache.swift-dummy.m │ │ ├── GRMustache.swift-prefix.pch │ │ ├── GRMustache.swift-umbrella.h │ │ ├── GRMustache.swift.xcconfig │ │ └── Info.plist │ └── Pods-WWDCCompanion │ │ ├── Pods-WWDCCompanion.modulemap │ │ ├── Pods-WWDCCompanion-dummy.m │ │ ├── Pods-WWDCCompanion-umbrella.h │ │ ├── Info.plist │ │ ├── Pods-WWDCCompanion.debug.xcconfig │ │ ├── Pods-WWDCCompanion.release.xcconfig │ │ ├── Pods-WWDCCompanion-acknowledgements.markdown │ │ ├── Pods-WWDCCompanion-acknowledgements.plist │ │ ├── Pods-WWDCCompanion-frameworks.sh │ │ └── Pods-WWDCCompanion-resources.sh ├── Fuzi │ ├── libxml2 │ │ ├── module.modulemap │ │ └── libxml2-fuzi.h │ ├── LICENSE │ ├── Sources │ │ ├── Error.swift │ │ ├── NodeSet.swift │ │ ├── Helpers.swift │ │ ├── Element.swift │ │ ├── Node.swift │ │ └── Document.swift │ └── README-ja.md ├── GRMustache.swift │ ├── Sources │ │ ├── LocatedTag.swift │ │ ├── Fixit-1.1.0.swift │ │ ├── VariableTag.swift │ │ ├── SectionTag.swift │ │ ├── Expression.swift │ │ ├── ExpressionInvocation.swift │ │ ├── TemplateAST.swift │ │ ├── ExpressionGenerator.swift │ │ ├── HTMLEscapeHelper.swift │ │ ├── URLEscapeHelper.swift │ │ ├── CoreGraphics.swift │ │ ├── ZipFilter.swift │ │ ├── TemplateToken.swift │ │ ├── Logger.swift │ │ ├── TemplateASTNode.swift │ │ ├── EachFilter.swift │ │ ├── TemplateGenerator.swift │ │ ├── Tag.swift │ │ ├── JavascriptEscapeHelper.swift │ │ ├── Formatter.swift │ │ ├── Common.swift │ │ └── StandardLibrary.swift │ ├── LICENSE │ └── ObjC │ │ ├── Mustache.h │ │ ├── GRMustacheKeyAccess.h │ │ └── GRMustacheKeyAccess.m ├── Manifest.lock └── Local Podspecs │ └── GRMustache.swift.podspec.json ├── WWDCCompanion.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── groue.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── xcshareddata │ └── xcschemes │ └── WWDCCompanion.xcscheme ├── WWDCCompanion.xcworkspace └── contents.xcworkspacedata ├── Podfile.lock ├── WWDCCompanion ├── AppDelegate.swift ├── Views │ ├── SessionTableViewCell.swift │ └── SearchResultTableViewCell.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── ImageCache.swift ├── Info.plist ├── session.html.mustache ├── Base.lproj │ └── LaunchScreen.storyboard ├── Controllers │ ├── SessionViewController.swift │ ├── SearchResultsTableViewController.swift │ └── SessionsTableViewController.swift └── Models │ ├── Database.swift │ └── Session.swift ├── Podfile ├── WWDCCompanionTests ├── Info.plist └── WWDCCompanionTests.swift ├── WWDCCompanionUITests ├── Info.plist └── WWDCCompanionUITests.swift ├── .gitignore └── README.md /Images/Screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groue/WWDCCompanion/HEAD/Images/Screen1.png -------------------------------------------------------------------------------- /Images/Screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groue/WWDCCompanion/HEAD/Images/Screen2.png -------------------------------------------------------------------------------- /Images/Screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groue/WWDCCompanion/HEAD/Images/Screen3.png -------------------------------------------------------------------------------- /Images/Screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groue/WWDCCompanion/HEAD/Images/Screen4.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GRDB"] 2 | path = GRDB 3 | url = https://github.com/groue/GRDB.swift.git 4 | -------------------------------------------------------------------------------- /GRDBCustomSQLite/GRDBCustomSQLite-USER.h: -------------------------------------------------------------------------------- 1 | // Expose custom SQLite configuration to framework users. 2 | 3 | #define SQLITE_ENABLE_FTS5 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Fuzi.modulemap: -------------------------------------------------------------------------------- 1 | framework module Fuzi { 2 | umbrella header "Fuzi-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Fuzi/libxml2/module.modulemap: -------------------------------------------------------------------------------- 1 | module libxml2 [system] { 2 | link "xml2" 3 | umbrella header "libxml2-fuzi.h" 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Fuzi-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Fuzi : NSObject 3 | @end 4 | @implementation PodsDummy_Fuzi 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/GRMustache.swift.modulemap: -------------------------------------------------------------------------------- 1 | framework module Mustache { 2 | umbrella header "GRMustache.swift-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/GRMustache.swift-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_GRMustache_swift : NSObject 3 | @end 4 | @implementation PodsDummy_GRMustache_swift 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_WWDCCompanion { 2 | umbrella header "Pods-WWDCCompanion-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_WWDCCompanion : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_WWDCCompanion 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Fuzi/libxml2/libxml2-fuzi.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | -------------------------------------------------------------------------------- /WWDCCompanion.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Fuzi-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/GRMustache.swift-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /WWDCCompanion.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/LocatedTag.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocatedTag.swift 3 | // Mustache 4 | // 5 | // Created by Gwendal Roué on 09/07/2015. 6 | // Copyright © 2015 Gwendal Roué. All rights reserved. 7 | // 8 | 9 | protocol LocatedTag: Tag { 10 | var templateID: TemplateID? { get } 11 | var lineNumber: Int { get } 12 | } 13 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Fuzi (1.0.1) 3 | - GRMustache.swift (2.0.0) 4 | 5 | DEPENDENCIES: 6 | - Fuzi (~> 1.0.0) 7 | - GRMustache.swift (~> 2.0.0) 8 | 9 | SPEC CHECKSUMS: 10 | Fuzi: 851403654bac55675aaeed815d0c8a1d3e3d3e8b 11 | GRMustache.swift: 8a547c12bea16052071b2b540af83d770075f0eb 12 | 13 | PODFILE CHECKSUM: 0789d4ac64484a7aa83b8eb343fee47241907322 14 | 15 | COCOAPODS: 1.3.1 16 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Fuzi (1.0.1) 3 | - GRMustache.swift (2.0.0) 4 | 5 | DEPENDENCIES: 6 | - Fuzi (~> 1.0.0) 7 | - GRMustache.swift (~> 2.0.0) 8 | 9 | SPEC CHECKSUMS: 10 | Fuzi: 851403654bac55675aaeed815d0c8a1d3e3d3e8b 11 | GRMustache.swift: 8a547c12bea16052071b2b540af83d770075f0eb 12 | 13 | PODFILE CHECKSUM: 0789d4ac64484a7aa83b8eb343fee47241907322 14 | 15 | COCOAPODS: 1.3.1 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Fuzi-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double FuziVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char FuziVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /WWDCCompanion/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | var window: UIWindow? 6 | 7 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 8 | try! setupDatabase(application) 9 | return true 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | use_frameworks! 3 | 4 | target 'WWDCCompanion' do 5 | pod 'Fuzi', '~> 1.0.0' 6 | pod 'GRMustache.swift', '~> 2.0.0' 7 | end 8 | 9 | post_install do |installer| 10 | installer.pods_project.targets.each do |target| 11 | target.build_configurations.each do |configuration| 12 | configuration.build_settings['SWIFT_VERSION'] = "3.2" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_WWDCCompanionVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_WWDCCompanionVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/GRMustache.swift-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "GRMustacheKeyAccess.h" 14 | #import "Mustache.h" 15 | 16 | FOUNDATION_EXPORT double MustacheVersionNumber; 17 | FOUNDATION_EXPORT const unsigned char MustacheVersionString[]; 18 | 19 | -------------------------------------------------------------------------------- /GRDBCustomSQLite/GRDBCustomSQLite-USER.xcconfig: -------------------------------------------------------------------------------- 1 | // GRDB configuration 2 | // 3 | // Convert CUSTOM_SQLLIBRARY_CFLAGS set in the SQLiteLib-USER.xcconfig to 4 | // OTHER_SWIFT_FLAGS by adding a space after the "-D" 5 | // 6 | // Example: 7 | // 8 | // // In SQLiteLib-USER.xcconfig 9 | // CUSTOM_SQLLIBRARY_CFLAGS = -DSQLITE_ENABLE_PREUPDATE_HOOK 10 | // 11 | // // In GRDBCustomSQLite-USER.xcconfig: 12 | // CUSTOM_OTHER_SWIFT_FLAGS = -D SQLITE_ENABLE_PREUPDATE_HOOK 13 | 14 | CUSTOM_OTHER_SWIFT_FLAGS = -D SQLITE_ENABLE_FTS5 15 | -------------------------------------------------------------------------------- /GRDBCustomSQLite/SQLiteLib-USER.xcconfig: -------------------------------------------------------------------------------- 1 | // SQLite configuration 2 | // 3 | // Custom additions to the SQLite compilation options 4 | // These are merged with the default options (for OSX / iOS) 5 | // 6 | // Example: 7 | // To build with SQLITE_ENABLE_PREUPDATE_HOOK set, 8 | // define the CUSTOM_SQLLIBRARY_CFLAGS line below: 9 | // 10 | // CUSTOM_SQLLIBRARY_CFLAGS = -DSQLITE_ENABLE_PREUPDATE_HOOK 11 | // 12 | // For more information on the options, see: https://www.sqlite.org/compile.html 13 | 14 | // Enable FTS5 15 | CUSTOM_SQLLIBRARY_CFLAGS = -DSQLITE_ENABLE_FTS5 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/GRMustache.swift.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/GRMustache.swift 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/GRMustache.swift 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Fuzi.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Fuzi 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" $(SDKROOT)/usr/include/libxml2 4 | OTHER_LDFLAGS = -l"xml2" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Fuzi 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | SWIFT_INCLUDE_PATHS = $(SRCROOT)/Fuzi/libxml2 13 | -------------------------------------------------------------------------------- /WWDCCompanion/Views/SessionTableViewCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class SessionTableViewCell: UITableViewCell { 4 | @IBOutlet private weak var sessionImageView: UIImageView! 5 | @IBOutlet weak var titleLabel: UILabel! 6 | @IBOutlet weak var focusesLabel: UILabel! 7 | 8 | var sessionImageURL: URL? { 9 | didSet { 10 | sessionImageView.image = nil 11 | if let url = sessionImageURL { 12 | ImageCache.default.loadImage(from: url) { [weak self] image in 13 | if self?.sessionImageURL == url { 14 | self?.sessionImageView.image = image 15 | } 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WWDCCompanionTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WWDCCompanionUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Pods/Local Podspecs/GRMustache.swift.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GRMustache.swift", 3 | "version": "1.1.0", 4 | "license": { 5 | "type": "MIT", 6 | "file": "LICENSE" 7 | }, 8 | "summary": "Flexible Mustache templates for Swift.", 9 | "homepage": "https://github.com/groue/GRMustache.swift", 10 | "authors": { 11 | "Gwendal Roué": "gr@pierlis.com" 12 | }, 13 | "source": { 14 | "git": "https://github.com/groue/GRMustache.swift.git", 15 | "tag": "1.1.0" 16 | }, 17 | "source_files": [ 18 | "Sources/**/*.{h,m,swift}", 19 | "ObjC/**/*.{h,m,swift}" 20 | ], 21 | "module_name": "Mustache", 22 | "platforms": { 23 | "ios": "8.0", 24 | "osx": "10.9", 25 | "tvos": "9.0" 26 | }, 27 | "requires_arc": true, 28 | "frameworks": "Foundation" 29 | } 30 | -------------------------------------------------------------------------------- /WWDCCompanion/Views/SearchResultTableViewCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class SearchResultTableViewCell: UITableViewCell { 4 | @IBOutlet private weak var sessionImageView: UIImageView! 5 | @IBOutlet weak var titleLabel: UILabel! 6 | @IBOutlet weak var focusesLabel: UILabel! 7 | @IBOutlet weak var snippetLabel: UILabel! 8 | 9 | var sessionImageURL: URL? { 10 | didSet { 11 | sessionImageView.image = nil 12 | if let url = sessionImageURL { 13 | ImageCache.default.loadImage(from: url) { [weak self] image in 14 | if self?.sessionImageURL == url { 15 | self?.sessionImageView.image = image 16 | } 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WWDCCompanion/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /WWDCCompanion/ImageCache.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ImageCache { 4 | static let `default` = ImageCache() 5 | private let session = URLSession(configuration: .default) 6 | private let cache = NSCache() 7 | 8 | func loadImage(from url: URL, completion: @escaping (UIImage) -> ()) { 9 | if let image = cache.object(forKey: url as NSURL) { 10 | completion(image) 11 | return 12 | } 13 | 14 | let task = session.dataTask(with: url) { [weak self] (data, response, error) in 15 | if let image = data.flatMap({ UIImage(data: $0) }) { 16 | DispatchQueue.main.async { 17 | self?.cache.setObject(image, forKey: url as NSURL) 18 | completion(image) 19 | } 20 | } 21 | } 22 | task.resume() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /WWDCCompanion.xcodeproj/xcuserdata/groue.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WWDCCompanion.xcscheme_^#shared#^_ 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | 15 | SuppressBuildableAutocreation 16 | 17 | 566D9A081DB0AE0F009CEA56 18 | 19 | primary 20 | 21 | 22 | 566D9A1C1DB0AE0F009CEA56 23 | 24 | primary 25 | 26 | 27 | 566D9A271DB0AE0F009CEA56 28 | 29 | primary 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Fuzi/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GRMustache.swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WWDCCompanionTests/WWDCCompanionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import WWDCCompanion 3 | 4 | class WWDCCompanionTests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | // Use XCTAssert and related functions to verify your tests produce the correct results. 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Fuzi" "$PODS_CONFIGURATION_BUILD_DIR/GRMustache.swift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(SDKROOT)/usr/include/libxml2 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Fuzi/Fuzi.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/GRMustache.swift/Mustache.framework/Headers" 7 | OTHER_LDFLAGS = $(inherited) -framework "Fuzi" -framework "Mustache" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | SWIFT_INCLUDE_PATHS = $(SRCROOT)/Fuzi/libxml2 14 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Fuzi" "$PODS_CONFIGURATION_BUILD_DIR/GRMustache.swift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(SDKROOT)/usr/include/libxml2 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Fuzi/Fuzi.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/GRMustache.swift/Mustache.framework/Headers" 7 | OTHER_LDFLAGS = $(inherited) -framework "Fuzi" -framework "Mustache" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | SWIFT_INCLUDE_PATHS = $(SRCROOT)/Fuzi/libxml2 14 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Gwendal Roué 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /Pods/Fuzi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ce Zheng 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /WWDCCompanionUITests/WWDCCompanionUITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class WWDCCompanionUITests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | 10 | // In UI tests it is usually best to stop immediately when a failure occurs. 11 | continueAfterFailure = false 12 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 13 | XCUIApplication().launch() 14 | 15 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // Use recording to get started writing UI tests. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /WWDCCompanion/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /WWDCCompanion/session.html.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 43 | 44 | 45 |
46 |
47 | 48 |
49 |

{{focuses}}

50 |

{{title}}

51 |
52 |
53 | {{#transcriptParagraphs}} 54 |

{{.}}

55 | {{/}} 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/ObjC/Mustache.h: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | //! Project version number for Mustache. 26 | FOUNDATION_EXPORT double MustacheVersionNumber; 27 | 28 | //! Project version string for Mustache. 29 | FOUNDATION_EXPORT const unsigned char MustacheVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | 33 | // IMPLEMENTATION NOTE 34 | // 35 | // This one should be private, but Xcode today requires ObjC headers that should 36 | // be available to private Swift code to be public. 37 | #import "GRMustacheKeyAccess.h" 38 | -------------------------------------------------------------------------------- /WWDCCompanion/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## https://github.com/github/gitignore/blob/master/Global/OSX.gitignore 2 | 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | ## https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 30 | 31 | # Windows image file caches 32 | Thumbs.db 33 | ehthumbs.db 34 | 35 | # Folder config file 36 | Desktop.ini 37 | 38 | # Recycle Bin used on file shares 39 | $RECYCLE.BIN/ 40 | 41 | # Windows Installer files 42 | *.cab 43 | *.msi 44 | *.msm 45 | *.msp 46 | 47 | # Windows shortcuts 48 | *.lnk 49 | 50 | ## https://github.com/github/gitignore/blob/master/Global/TextMate.gitignore 51 | 52 | *.tmproj 53 | *.tmproject 54 | tmtags 55 | 56 | ## https://github.com/github/gitignore/blob/master/Global/Vim.gitignore 57 | 58 | [._]*.s[a-w][a-z] 59 | [._]s[a-w][a-z] 60 | *.un~ 61 | Session.vim 62 | .netrwhist 63 | *~ 64 | 65 | ## https://github.com/github/gitignore/blob/master/Objective-C.gitignore 66 | 67 | # Xcode 68 | # 69 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 70 | 71 | # Build generated 72 | build/ 73 | DerivedData 74 | 75 | # Various settings 76 | *.pbxuser 77 | !default.pbxuser 78 | *.mode1v3 79 | !default.mode1v3 80 | *.mode2v3 81 | !default.mode2v3 82 | *.perspectivev3 83 | !default.perspectivev3 84 | xcuserdata 85 | 86 | # Other 87 | *.xccheckout 88 | *.moved-aside 89 | *.xcuserstate 90 | *.xcscmblueprint 91 | 92 | # Obj-C/Swift specific 93 | *.hmap 94 | *.ipa 95 | 96 | # Performance test baselines 97 | *.xcbaseline 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WWDC Companion 2 | ============== 3 | 4 | **This Swift 3 application stores, displays, and lets the user search the [WWDC 2017 sessions](https://developer.apple.com/videos/wwdc2017/).** 5 | 6 | | | | | | 7 | | :-----: | :-----: | :-----: | :-----: | 8 | | ![Screen shot 1](Images/Screen1.png) | ![Screen shot 2](Images/Screen2.png) | ![Screen shot 3](Images/Screen3.png) | ![Screen shot 4](Images/Screen4.png) | 9 | 10 | ### How to run the app 11 | 12 | - Clone the WWDCCompanion repository 13 | - Execute: 14 | 15 | ```sh 16 | cd WWDCCompanion 17 | git submodule update --init GRDB 18 | cd GRDB 19 | git submodule update --init SQLiteCustom/src 20 | ``` 21 | - Open WWDCCompanion.xcworkspace 22 | - Run the WWDCCompanion target 23 | 24 | ### What’s in this demo? 25 | 26 | - **Perform full-text search** in an SQLite database with [GRDB.swift](http://github.com/groue/GRDB.swift) 27 | - [Database.swift](WWDCCompanion/Models/Database.swift) initializes the SQLite database. 28 | - [Session](WWDCCompanion/Models/Session.swift) is the GRDB [record](https://github.com/groue/GRDB.swift#records) that allows fetching and saving WWDC sessions in the database. 29 | - [SessionsTableViewController](WWDCCompanion/Controllers/SessionsTableViewController.swift) synchronizes its table view with the content of the database with a [fetched records controller](https://github.com/groue/GRDB.swift#fetchedrecordscontroller). 30 | - [SearchResultsTableViewController](WWDCCompanion/Controllers/SearchResultsTableViewController.swift) performs full-text search. 31 | 32 | - **Install GRDB with a custom build of SQLite** 33 | 34 | - **Render HTML templates** with [GRMustache.swift](https://github.com/groue/GRMustache.swift) 35 | - [SessionViewController](WWDCCompanion/Controllers/SessionViewController.swift) renders an HTML template that displays a WWDC session. 36 | 37 | - **Parse HTML** with [Fuzi](https://github.com/cezheng/Fuzi) 38 | -------------------------------------------------------------------------------- /GRDBCustomSQLite/setup.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Sync USER Configuration Files 3 | # 4 | # Last Updated: 2016-05-31 (A) 5 | # 6 | # License: MIT License 7 | # https://github.com/swiftlyfalling/SQLiteLib/blob/master/LICENSE 8 | # 9 | ####################################################### 10 | # PROJECT PATHS 11 | # !! MODIFY THESE TO MATCH YOUR PROJECT HIERARCHY !! 12 | ####################################################### 13 | 14 | # The path to the folder containing GRDB.xcodeproj: 15 | GRDB_SOURCE_PATH="${PROJECT_DIR}/GRDB" 16 | 17 | # The path to your custom "SQLiteLib-USER.xcconfig": 18 | SQLITELIB_XCCONFIG_USER_PATH="${PROJECT_DIR}/GRDBCustomSQLite/SQLiteLib-USER.xcconfig" 19 | 20 | # The path to your custom "GRDBCustomSQLite-USER.xcconfig": 21 | CUSTOMSQLITE_XCCONFIG_USER_PATH="${PROJECT_DIR}/GRDBCustomSQLite/GRDBCustomSQLite-USER.xcconfig" 22 | 23 | # The path to your custom "GRDBCustomSQLite-USER.h": 24 | CUSTOMSQLITE_H_USER_PATH="${PROJECT_DIR}/GRDBCustomSQLite/GRDBCustomSQLite-USER.h" 25 | 26 | ####################################################### 27 | # 28 | ####################################################### 29 | 30 | 31 | if [ ! -d "$GRDB_SOURCE_PATH" ]; 32 | then 33 | echo "error: Path to GRDB source (GRDB_SOURCE_PATH) missing/incorrect: $GRDB_SOURCE_PATH" 34 | exit 1 35 | fi 36 | 37 | SyncFileChanges () { 38 | SOURCE=$1 39 | DESTINATIONPATH=$2 40 | DESTINATIONFILENAME=$3 41 | DESTINATION="${DESTINATIONPATH}/${DESTINATIONFILENAME}" 42 | 43 | if [ ! -f "$SOURCE" ]; 44 | then 45 | echo "error: Source file missing: $SOURCE" 46 | exit 1 47 | fi 48 | 49 | rsync -a "$SOURCE" "$DESTINATION" 50 | } 51 | 52 | SyncFileChanges $SQLITELIB_XCCONFIG_USER_PATH "${GRDB_SOURCE_PATH}/SQLiteCustom/src" "SQLiteLib-USER.xcconfig" 53 | SyncFileChanges $CUSTOMSQLITE_XCCONFIG_USER_PATH "${GRDB_SOURCE_PATH}/SQLiteCustom" "GRDBCustomSQLite-USER.xcconfig" 54 | SyncFileChanges $CUSTOMSQLITE_H_USER_PATH "${GRDB_SOURCE_PATH}/SQLiteCustom" "GRDBCustomSQLite-USER.h" 55 | 56 | echo "Finished syncing" 57 | -------------------------------------------------------------------------------- /WWDCCompanion/Controllers/SessionViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import WebKit 3 | import Mustache 4 | 5 | class SessionViewController: UIViewController { 6 | private var webView: WKWebView! 7 | var session: Session! 8 | 9 | override func viewDidLoad() { 10 | super.viewDidLoad() 11 | 12 | webView = WKWebView(frame: view.bounds) 13 | webView.translatesAutoresizingMaskIntoConstraints = true 14 | webView.autoresizingMask = [.flexibleHeight, .flexibleWidth] 15 | webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal 16 | view.addSubview(webView) 17 | } 18 | 19 | override func viewWillAppear(_ animated: Bool) { 20 | super.viewWillAppear(animated) 21 | 22 | let transcriptParagraphs = session 23 | .transcript 24 | .characters 25 | .split(separator: "\n") 26 | .map { String($0) } 27 | 28 | let calloutFont = UIFont.preferredFont(forTextStyle: .callout) 29 | let bodyFont = UIFont.preferredFont(forTextStyle: .body) 30 | let title1Font = UIFont.preferredFont(forTextStyle: .title1) 31 | 32 | let template = try! Template(named: "session.html") 33 | let templateValue: [String : Any] = [ 34 | "calloutFont": [ 35 | "name": calloutFont.familyName, 36 | "size": calloutFont.pointSize], 37 | "bodyFont": [ 38 | "name": bodyFont.familyName, 39 | "size": bodyFont.pointSize], 40 | "title1Font": [ 41 | "name": title1Font.familyName, 42 | "size": title1Font.pointSize], 43 | "sessionImageURL": session.imageURL.absoluteString, 44 | "title": session.title, 45 | "focuses": session.focuses, 46 | "transcriptParagraphs": transcriptParagraphs, 47 | ] 48 | let html = try! template.render(templateValue) 49 | webView.loadHTMLString(html, baseURL: nil) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Pods/Fuzi/Sources/Error.swift: -------------------------------------------------------------------------------- 1 | // Error.swift 2 | // Copyright (c) 2015 Ce Zheng 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | import libxml2 24 | 25 | /** 26 | * XMLError enumeration. 27 | */ 28 | public enum XMLError: Error { 29 | /// No error 30 | case noError 31 | /// Contains a libxml2 error with error code and message 32 | case libXMLError(code: Int, message: String) 33 | /// Failed to convert String to bytes using given string encoding 34 | case invalidData 35 | /// XML Parser failed to parse the document 36 | case parserFailure 37 | 38 | internal static func lastError(defaultError: XMLError = .noError) -> XMLError { 39 | guard let errorPtr = xmlGetLastError() else { 40 | return defaultError 41 | } 42 | let message = (^-^errorPtr.pointee.message)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 43 | let code = Int(errorPtr.pointee.code) 44 | xmlResetError(errorPtr) 45 | return .libXMLError(code: code, message: message ?? "") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/ObjC/GRMustacheKeyAccess.h: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | // IMPLEMENTATION NOTE 26 | // 27 | // This code comes from Objective-C GRMustache. 28 | // 29 | // It is still written in Objective-C because 30 | // +[GRMustacheKeyAccess isSafeMustacheKey:forObject:] used to need the 31 | // [[object class] safeMustacheKeys] for classes that would conform to the 32 | // now removed GRMustacheSafeKeyAccess protocol. 33 | // 34 | // Swift would not let us do that (see example below): 35 | // 36 | // :: 37 | // 38 | // import Foundation 39 | // 40 | // @objc protocol P { 41 | // static func f() -> String 42 | // } 43 | // 44 | // class C : NSObject, P { 45 | // class func f() -> String { return "C" } 46 | // } 47 | // 48 | // // Expect "C", But we get the error: 49 | // // accessing members of protocol type value 'P.Type' is unimplemented 50 | // (C.self as P.Type).f() 51 | // 52 | @interface GRMustacheKeyAccess : NSObject 53 | + (BOOL)isSafeMustacheKey:(NSString *)key forObject:(id)object; 54 | @end 55 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/Fixit-1.1.0.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | @available(*, unavailable, message:"Use nil instead.") 25 | public func Box() -> MustacheBox { return EmptyBox } 26 | 27 | extension Template { 28 | @available(*, unavailable, renamed:"register(_:forKey:)") 29 | public func registerInBaseContext(_ key: String, _ value: Any?) { } 30 | } 31 | 32 | extension Context { 33 | @available(*, unavailable, renamed:"mustacheBox(forKey:)") 34 | public func mustacheBoxForKey(_ key: String) -> MustacheBox { return EmptyBox } 35 | 36 | @available(*, unavailable, renamed:"mustacheBox(forExpression:)") 37 | public func mustacheBoxForExpression(_ string: String) throws -> MustacheBox { return EmptyBox } 38 | 39 | @available(*, unavailable, renamed:"extendedContext(withRegisteredValue:forKey:)") 40 | func contextWithRegisteredKey(_ key: String, box: MustacheBox) -> Context { return self } 41 | } 42 | 43 | extension MustacheBox { 44 | @nonobjc @available(*, unavailable, renamed:"mustacheBox(forKey:)") 45 | public func mustacheBoxForKey(_ key: String) -> MustacheBox { return EmptyBox } 46 | } 47 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/VariableTag.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | /// VariableTag represents a variable tag such as {{name}} or {{{name}}}. 27 | final class VariableTag: LocatedTag { 28 | let token: TemplateToken 29 | let contentType: ContentType 30 | 31 | init(contentType: ContentType, token: TemplateToken) { 32 | self.contentType = contentType 33 | self.token = token 34 | } 35 | 36 | // Mark: - Tag protocol 37 | 38 | let type: TagType = .variable 39 | let innerTemplateString: String = "" 40 | var tagDelimiterPair: TagDelimiterPair { return token.tagDelimiterPair! } 41 | 42 | var description: String { 43 | return "\(token.templateSubstring) at \(token.locationDescription)" 44 | } 45 | 46 | // Variable have no inner content. 47 | func render(_ context: Context) throws -> Rendering { 48 | return Rendering("", contentType) 49 | } 50 | 51 | // Mark: - LocatedTag 52 | 53 | var templateID: TemplateID? { return token.templateID } 54 | var lineNumber: Int { return token.lineNumber } 55 | } 56 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Fuzi 5 | 6 | Copyright (c) 2015 Ce Zheng 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## GRMustache.swift 28 | 29 | Copyright (C) 2014 Gwendal Roué 30 | 31 | 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: 32 | 33 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 34 | 35 | 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. 36 | 37 | Generated by CocoaPods - https://cocoapods.org 38 | -------------------------------------------------------------------------------- /WWDCCompanion/Models/Database.swift: -------------------------------------------------------------------------------- 1 | import GRDBCustomSQLite 2 | import UIKit 3 | 4 | var dbQueue: DatabaseQueue! 5 | 6 | func setupDatabase(_ application: UIApplication) throws { 7 | 8 | // Connect to the database 9 | // See https://github.com/groue/GRDB.swift/#database-connections 10 | 11 | let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as NSString 12 | let databasePath = documentsPath.appendingPathComponent("db.sqlite") 13 | dbQueue = try DatabaseQueue(path: databasePath) 14 | 15 | 16 | // Be a nice iOS citizen, and don't consume too much memory 17 | // See https://github.com/groue/GRDB.swift/#memory-management 18 | 19 | dbQueue.setupMemoryManagement(in: application) 20 | 21 | 22 | // Use DatabaseMigrator to setup the database 23 | // See https://github.com/groue/GRDB.swift/#migrations 24 | 25 | var migrator = DatabaseMigrator() 26 | 27 | migrator.registerMigration("createWWDCSessions") { db in 28 | 29 | try db.create(table: "sessions") { t in 30 | t.primaryKey(["year", "number"]) 31 | t.column("year", .integer).notNull() 32 | t.column("number", .integer).notNull() 33 | t.column("collection", .text).notNull() 34 | t.column("title", .text).notNull() 35 | t.column("description", .text).notNull() 36 | t.column("transcript", .text).notNull() 37 | t.column("iOS", .boolean).notNull() 38 | t.column("macOS", .boolean).notNull() 39 | t.column("tvOS", .boolean).notNull() 40 | t.column("watchOS", .boolean).notNull() 41 | t.column("sessionURL", .text).notNull() 42 | t.column("imageURL", .text).notNull() 43 | t.column("videoURL", .text) 44 | t.column("presentationURL", .text) 45 | } 46 | 47 | try db.create(virtualTable: "fullTextSessions", using: FTS5()) { t in 48 | // Porter tokenizer provides English stemming 49 | t.tokenizer = .porter() 50 | 51 | // Index the content of the sessions table 52 | // See https://github.com/groue/GRDB.swift#external-content-full-text-tables 53 | t.synchronize(withTable: "sessions") 54 | 55 | // The indexed columns 56 | t.column("title") 57 | t.column("transcript") 58 | t.column("description") 59 | } 60 | } 61 | 62 | try migrator.migrate(dbQueue) 63 | } 64 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/SectionTag.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | /// A SectionTag represents a regular or inverted section tag such as 27 | /// {{#section}}...{{/section}} or {{^section}}...{{/section}}. 28 | final class SectionTag: LocatedTag { 29 | let openingToken: TemplateToken 30 | let innerTemplateAST: TemplateAST 31 | 32 | init(innerTemplateAST: TemplateAST, openingToken: TemplateToken, innerTemplateString: String) { 33 | self.innerTemplateAST = innerTemplateAST 34 | self.openingToken = openingToken 35 | self.innerTemplateString = innerTemplateString 36 | } 37 | 38 | // Mark: - Tag protocol 39 | 40 | let type: TagType = .section 41 | let innerTemplateString: String 42 | var tagDelimiterPair: TagDelimiterPair { return openingToken.tagDelimiterPair! } 43 | 44 | var description: String { 45 | return "\(openingToken.templateSubstring) at \(openingToken.locationDescription)" 46 | } 47 | 48 | func render(_ context: Context) throws -> Rendering { 49 | let renderingEngine = RenderingEngine(templateAST: innerTemplateAST, context: context) 50 | return try renderingEngine.render() 51 | } 52 | 53 | // Mark: - LocatedTag 54 | 55 | var templateID: TemplateID? { return openingToken.templateID } 56 | var lineNumber: Int { return openingToken.lineNumber } 57 | } 58 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/Expression.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | /// The type for expressions that appear in tags: `name`, `user.name`, 27 | /// `uppercase(user.name)`, etc. 28 | enum Expression { 29 | 30 | // {{ . }} 31 | case implicitIterator 32 | 33 | // {{ identifier }} 34 | case identifier(identifier: String) 35 | 36 | // {{ .identifier }} 37 | indirect case scoped(baseExpression: Expression, identifier: String) 38 | 39 | // {{ () }} 40 | indirect case filter(filterExpression: Expression, argumentExpression: Expression, partialApplication: Bool) 41 | } 42 | 43 | /// Expression conforms to Equatable so that the Compiler can check that section 44 | /// tags have matching openings and closings: {{# person }}...{{/ person }} is 45 | /// OK but {{# foo }}...{{/ bar }} is not. 46 | extension Expression: Equatable { 47 | } 48 | 49 | func ==(lhs: Expression, rhs: Expression) -> Bool { 50 | switch (lhs, rhs) { 51 | case (.implicitIterator, .implicitIterator): 52 | return true 53 | 54 | case (.identifier(let lIdentifier), .identifier(let rIdentifier)): 55 | return lIdentifier == rIdentifier 56 | 57 | case (.scoped(let lBase, let lIdentifier), .scoped(let rBase, let rIdentifier)): 58 | return lBase == rBase && lIdentifier == rIdentifier 59 | 60 | case (.filter(let lFilter, let lArgument, let lPartialApplication), .filter(let rFilter, let rArgument, let rPartialApplication)): 61 | return lFilter == rFilter && lArgument == rArgument && lPartialApplication == rPartialApplication 62 | 63 | default: 64 | return false 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /WWDCCompanion/Models/Session.swift: -------------------------------------------------------------------------------- 1 | import GRDBCustomSQLite 2 | 3 | class Session : Record { 4 | let year: Int 5 | let number: Int 6 | let collection: String 7 | let title: String 8 | let description: String 9 | let transcript: String 10 | let iOS: Bool 11 | let macOS: Bool 12 | let tvOS: Bool 13 | let watchOS: Bool 14 | let sessionURL: URL 15 | let imageURL: URL 16 | let videoURL: URL? 17 | let presentationURL: URL? 18 | 19 | var focuses: String { 20 | var focuses: [String] = [] 21 | if iOS { focuses.append("iOS") } 22 | if macOS { focuses.append("macOS") } 23 | if tvOS { focuses.append("tvOS") } 24 | if watchOS { focuses.append("watchOS") } 25 | return focuses.joined(separator: ", ") 26 | 27 | } 28 | 29 | init(year: Int, number: Int, collection: String, title: String, description: String, transcript: String, iOS: Bool, macOS: Bool, tvOS: Bool, watchOS: Bool, sessionURL: URL, imageURL: URL, videoURL: URL?, presentationURL: URL?) { 30 | self.year = year 31 | self.number = number 32 | self.collection = collection 33 | self.title = title 34 | self.description = description 35 | self.transcript = transcript 36 | self.iOS = iOS 37 | self.macOS = macOS 38 | self.tvOS = tvOS 39 | self.watchOS = watchOS 40 | self.sessionURL = sessionURL 41 | self.imageURL = imageURL 42 | self.videoURL = videoURL 43 | self.presentationURL = presentationURL 44 | super.init() 45 | } 46 | 47 | // MARK: - Record 48 | 49 | override class var databaseTableName: String { return "sessions" } 50 | 51 | required init(row: Row) { 52 | year = row["year"] 53 | number = row["number"] 54 | collection = row["collection"] 55 | title = row["title"] 56 | description = row["description"] 57 | transcript = row["transcript"] 58 | iOS = row["iOS"] 59 | macOS = row["macOS"] 60 | tvOS = row["tvOS"] 61 | watchOS = row["watchOS"] 62 | sessionURL = row["sessionURL"] 63 | imageURL = row["imageURL"] 64 | videoURL = row["videoURL"] 65 | presentationURL = row["presentationURL"] 66 | super.init(row: row) 67 | } 68 | 69 | override func encode(to container: inout PersistenceContainer) { 70 | container["year"] = year 71 | container["number"] = number 72 | container["collection"] = collection 73 | container["title"] = title 74 | container["description"] = description 75 | container["transcript"] = transcript 76 | container["iOS"] = iOS 77 | container["macOS"] = macOS 78 | container["watchOS"] = watchOS 79 | container["tvOS"] = tvOS 80 | container["sessionURL"] = sessionURL 81 | container["imageURL"] = imageURL 82 | container["videoURL"] = videoURL 83 | container["presentationURL"] = presentationURL 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/ExpressionInvocation.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | struct ExpressionInvocation { 27 | let expression: Expression 28 | 29 | func invokeWithContext(_ context: Context) throws -> MustacheBox { 30 | return try evaluate(context: context, expression: expression) 31 | } 32 | 33 | fileprivate func evaluate(context: Context, expression: Expression) throws -> MustacheBox { 34 | switch expression { 35 | case .implicitIterator: 36 | // {{ . }} 37 | 38 | return context.topBox 39 | 40 | case .identifier(let identifier): 41 | // {{ identifier }} 42 | 43 | return context.mustacheBox(forKey: identifier) 44 | 45 | case .scoped(let baseExpression, let identifier): 46 | // {{ .identifier }} 47 | 48 | return try evaluate(context: context, expression: baseExpression).mustacheBox(forKey: identifier) 49 | 50 | case .filter(let filterExpression, let argumentExpression, let partialApplication): 51 | // {{ () }} 52 | 53 | let filterBox = try evaluate(context: context, expression: filterExpression) 54 | 55 | guard let filter = filterBox.filter else { 56 | if filterBox.isEmpty { 57 | throw MustacheError(kind: .renderError, message: "Missing filter") 58 | } else { 59 | throw MustacheError(kind: .renderError, message: "Not a filter") 60 | } 61 | } 62 | 63 | let argumentBox = try evaluate(context: context, expression: argumentExpression) 64 | return try Box(filter(argumentBox, partialApplication)) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/TemplateAST.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | /// The abstract syntax tree of a template 25 | final class TemplateAST { 26 | 27 | /// A template AST can be "defined" or "undefined". 28 | /// 29 | /// Undefined template ASTs are used when parsing templates which embed a 30 | /// partial tag which refers to themselves. The compiler would emit a 31 | /// PartialNode which contains a reference to an undefined (yet) template 32 | /// AST. At the end of the compilation the undefined template AST would 33 | /// become defined. 34 | /// 35 | /// See TemplateRepository.templateAST(named:relativeToTemplateID:error:). 36 | enum `Type` { 37 | case undefined 38 | case defined(nodes: [TemplateASTNode], contentType: ContentType) 39 | } 40 | var type: Type 41 | 42 | fileprivate init(type: Type) { 43 | self.type = type 44 | } 45 | 46 | 47 | /// Creates an undefined TemplateAST. 48 | convenience init() { 49 | self.init(type: Type.undefined) 50 | } 51 | 52 | /// Creates a defined TemplateAST. 53 | convenience init(nodes: [TemplateASTNode], contentType: ContentType) { 54 | self.init(type: Type.defined(nodes: nodes, contentType: contentType)) 55 | } 56 | 57 | /// Nil if the template AST is undefined. 58 | var nodes: [TemplateASTNode]! { 59 | switch type { 60 | case .undefined: 61 | return nil 62 | case .defined(let nodes, _): 63 | return nodes 64 | } 65 | } 66 | 67 | /// Nil if the template AST is undefined. 68 | var contentType: ContentType! { 69 | switch type { 70 | case .undefined: 71 | return nil 72 | case .defined(_, let contentType): 73 | return contentType 74 | } 75 | } 76 | 77 | func updateFromTemplateAST(_ templateAST: TemplateAST) { 78 | self.type = templateAST.type 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/ExpressionGenerator.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | extension Expression : CustomDebugStringConvertible { 25 | /// A textual representation of `self`, suitable for debugging. 26 | var debugDescription: String { 27 | let string = ExpressionGenerator().stringFromExpression(self) 28 | return "Expression(\(string))" 29 | } 30 | } 31 | 32 | final class ExpressionGenerator { 33 | let configuration: Configuration 34 | 35 | init(configuration: Configuration? = nil) { 36 | self.configuration = configuration ?? DefaultConfiguration 37 | } 38 | 39 | func stringFromExpression(_ expression: Expression) -> String { 40 | buffer = "" 41 | renderExpression(expression) 42 | return buffer 43 | } 44 | 45 | func renderExpression(_ expression: Expression) { 46 | switch expression { 47 | case .implicitIterator: 48 | // {{ . }} 49 | 50 | buffer.append(".") 51 | 52 | case .identifier(let identifier): 53 | // {{ identifier }} 54 | 55 | buffer.append(identifier) 56 | 57 | case .scoped(let baseExpression, let identifier): 58 | // {{ .identifier }} 59 | 60 | renderExpression(baseExpression) 61 | buffer.append(".") 62 | buffer.append(identifier) 63 | 64 | case .filter(let filterExpression, let argumentExpression, _): 65 | // {{ () }} 66 | // 67 | // Support for variadic filters is not implemented: 68 | // `f(a,b)` is rendered `f(a)(b)`. 69 | 70 | renderExpression(filterExpression) 71 | buffer.append("(") 72 | renderExpression(argumentExpression) 73 | buffer.append(")") 74 | } 75 | } 76 | 77 | fileprivate var buffer: String = "" 78 | } 79 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/HTMLEscapeHelper.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | final class HTMLEscapeHelper : MustacheBoxable { 27 | 28 | var mustacheBox: MustacheBox { 29 | // Return a multi-facetted box, because HTMLEscape interacts in 30 | // various ways with Mustache rendering. 31 | return MustacheBox( 32 | // It has a value: 33 | value: self, 34 | 35 | // HTMLEscape can be used as a filter: {{ HTMLEscape(x) }}: 36 | filter: Filter(filter), 37 | 38 | // HTMLEscape escapes all variable tags: {{# HTMLEscape }}...{{ x }}...{{/ HTMLEscape }} 39 | willRender: willRender) 40 | } 41 | 42 | // This function is used for evaluating `HTMLEscape(x)` expressions. 43 | private func filter(_ rendering: Rendering) throws -> Rendering { 44 | return Rendering(escapeHTML(rendering.string), rendering.contentType) 45 | } 46 | 47 | // A WillRenderFunction: this function lets HTMLEscape change values that 48 | // are about to be rendered to their escaped counterpart. 49 | // 50 | // It is activated as soon as the formatter enters the context stack, when 51 | // used in a section {{# HTMLEscape }}...{{/ HTMLEscape }}. 52 | private func willRender(_ tag: Tag, box: MustacheBox) -> Any? { 53 | switch tag.type { 54 | case .variable: 55 | // {{ value }} 56 | // We don't know if the box contains a String, so let's escape its 57 | // rendering. 58 | return { (info: RenderingInfo) -> Rendering in 59 | let rendering = try box.render(info) 60 | return try self.filter(rendering) 61 | } 62 | case .section: 63 | // {{# value }}...{{/ value }} 64 | // {{^ value }}...{{/ value }} 65 | // Leave sections untouched, so that loops and conditions are not 66 | // affected by the formatter. 67 | 68 | return box 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/URLEscapeHelper.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | final class URLEscapeHelper : MustacheBoxable { 27 | 28 | var mustacheBox: MustacheBox { 29 | // Return a multi-facetted box, because URLEscape interacts in 30 | // various ways with Mustache rendering. 31 | return MustacheBox( 32 | // It has a value: 33 | value: self, 34 | 35 | // URLEscape can be used as a filter: {{ URLEscape(x) }}: 36 | filter: Filter(filter), 37 | 38 | // URLEscape escapes all variable tags: {{# URLEscape }}...{{ x }}...{{/ URLEscape }} 39 | willRender: willRender) 40 | } 41 | 42 | // This function is used for evaluating `URLEscape(x)` expressions. 43 | fileprivate func filter(_ rendering: Rendering) throws -> Rendering { 44 | return Rendering(URLEscapeHelper.escapeURL(rendering.string), rendering.contentType) 45 | } 46 | 47 | // A WillRenderFunction: this function lets URLEscape change values that 48 | // are about to be rendered to their escaped counterpart. 49 | // 50 | // It is activated as soon as the formatter enters the context stack, when 51 | // used in a section {{# URLEscape }}...{{/ URLEscape }}. 52 | fileprivate func willRender(_ tag: Tag, box: MustacheBox) -> Any? { 53 | switch tag.type { 54 | case .variable: 55 | // We don't know if the box contains a String, so let's escape its 56 | // rendering. 57 | return { (info: RenderingInfo) -> Rendering in 58 | let rendering = try box.render(info) 59 | return try self.filter(rendering) 60 | } 61 | case .section: 62 | return box 63 | } 64 | } 65 | 66 | fileprivate class func escapeURL(_ string: String) -> String { 67 | let s = (CharacterSet.urlQueryAllowed as NSCharacterSet).mutableCopy() as! NSMutableCharacterSet 68 | s.removeCharacters(in: "?&=") 69 | return string.addingPercentEncoding(withAllowedCharacters: s as CharacterSet) ?? "" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/CoreGraphics.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2016 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) 24 | import CoreGraphics 25 | 26 | /// GRMustache provides built-in support for rendering `CGFloat`. 27 | extension CGFloat : MustacheBoxable { 28 | 29 | /// CGFloat adopts the MustacheBoxable protocol so that it can feed 30 | /// Mustache templates. 31 | /// 32 | /// You should not directly call the `mustacheBox` property. 33 | /// 34 | /// ### Rendering 35 | /// 36 | /// - `{{cgfloat}}` is rendered with built-in Swift String 37 | /// Interpolation. Custom formatting can be explicitly required with 38 | /// NSNumberFormatter, as in `{{format(a)}}` (see `NSFormatter`). 39 | /// 40 | /// - `{{#cgfloat}}...{{/cgfloat}}` renders if and only if `cgfloat` is not 0 (zero). 41 | /// 42 | /// - `{{^cgfloat}}...{{/cgfloat}}` renders if and only if `double` is 0 (zero). 43 | public var mustacheBox: MustacheBox { 44 | return MustacheBox( 45 | value: self, 46 | boolValue: (self != 0.0), 47 | render: { (info: RenderingInfo) in 48 | switch info.tag.type { 49 | case .variable: 50 | // {{ cgfloat }} 51 | return Rendering("\(self)") 52 | case .section: 53 | if info.enumerationItem { 54 | // {{# cgfloats }}...{{/ cgfloats }} 55 | return try info.tag.render(info.context.extendedContext(Box(self))) 56 | } else { 57 | // {{# cgfloat }}...{{/ cgfloat }} 58 | // 59 | // Doubles do not enter the context stack when used in a 60 | // boolean section. 61 | // 62 | // This behavior must not change: 63 | // https://github.com/groue/GRMustache/issues/83 64 | return try info.tag.render(info.context) 65 | } 66 | } 67 | }) 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/ZipFilter.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | let ZipFilter = VariadicFilter { (boxes) in 27 | 28 | // Turn collection arguments into iterators. Iterators can be iterated 29 | // all together, and this is what we need. 30 | // 31 | // Other kinds of arguments generate an error. 32 | 33 | var zippedIterators: [AnyIterator] = [] 34 | 35 | for box in boxes { 36 | if box.isEmpty { 37 | // Missing collection does not provide anything 38 | } else if let array = box.arrayValue { 39 | // Array 40 | zippedIterators.append(AnyIterator(array.makeIterator())) 41 | } else { 42 | // Error 43 | throw MustacheError(kind: .renderError, message: "Non-enumerable argument in zip filter: `\(box.value)`") 44 | } 45 | } 46 | 47 | 48 | // Build an array of custom render functions 49 | 50 | var renderFunctions: [RenderFunction] = [] 51 | 52 | while true { 53 | 54 | // Extract from all iterators the boxes that should enter the rendering 55 | // context at each iteration. 56 | // 57 | // Given the [1,2,3], [a,b,c] input collections, those boxes would be 58 | // [1,a] then [2,b] and finally [3,c]. 59 | 60 | var zippedBoxes: [MustacheBox] = [] 61 | for iterator in zippedIterators { 62 | var iterator = iterator 63 | if let box = iterator.next() { 64 | zippedBoxes.append(box) 65 | } 66 | } 67 | 68 | 69 | // All iterators have been enumerated: stop 70 | 71 | if zippedBoxes.isEmpty { 72 | break; 73 | } 74 | 75 | 76 | // Build a render function which extends the rendering context with 77 | // zipped boxes before rendering the tag. 78 | 79 | let renderFunction: RenderFunction = { (info) -> Rendering in 80 | var context = zippedBoxes.reduce(info.context) { (context, box) in context.extendedContext(box) } 81 | return try info.tag.render(context) 82 | } 83 | 84 | renderFunctions.append(renderFunction) 85 | } 86 | 87 | return renderFunctions 88 | } 89 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/TemplateToken.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | struct TemplateToken { 25 | enum `Type` { 26 | /// text 27 | case text(text: String) 28 | 29 | /// {{ content }} 30 | case escapedVariable(content: String, tagDelimiterPair: TagDelimiterPair) 31 | 32 | /// {{{ content }}} 33 | case unescapedVariable(content: String, tagDelimiterPair: TagDelimiterPair) 34 | 35 | /// {{! comment }} 36 | case comment 37 | 38 | /// {{# content }} 39 | case section(content: String, tagDelimiterPair: TagDelimiterPair) 40 | 41 | /// {{^ content }} 42 | case invertedSection(content: String, tagDelimiterPair: TagDelimiterPair) 43 | 44 | /// {{/ content }} 45 | case close(content: String) 46 | 47 | /// {{> content }} 48 | case partial(content: String) 49 | 50 | /// {{= ... ... =}} 51 | case setDelimiters 52 | 53 | /// {{% content }} 54 | case pragma(content: String) 55 | 56 | /// {{< content }} 57 | case partialOverride(content: String) 58 | 59 | /// {{$ content }} 60 | case block(content: String) 61 | } 62 | 63 | let type: Type 64 | let lineNumber: Int 65 | let templateID: TemplateID? 66 | let templateString: String 67 | let range: Range 68 | 69 | var templateSubstring: String { return templateString[range] } 70 | 71 | var tagDelimiterPair: TagDelimiterPair? { 72 | switch type { 73 | case .escapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair): 74 | return tagDelimiterPair 75 | case .unescapedVariable(content: _, tagDelimiterPair: let tagDelimiterPair): 76 | return tagDelimiterPair 77 | case .section(content: _, tagDelimiterPair: let tagDelimiterPair): 78 | return tagDelimiterPair 79 | case .invertedSection(content: _, tagDelimiterPair: let tagDelimiterPair): 80 | return tagDelimiterPair 81 | default: 82 | return nil 83 | } 84 | } 85 | 86 | var locationDescription: String { 87 | if let templateID = templateID { 88 | return "line \(lineNumber) of template \(templateID)" 89 | } else { 90 | return "line \(lineNumber)" 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2015 Ce Zheng 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Fuzi 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Copyright (C) 2014 Gwendal Roué 47 | 48 | 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: 49 | 50 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 51 | 52 | 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. 53 | 54 | License 55 | MIT 56 | Title 57 | GRMustache.swift 58 | Type 59 | PSGroupSpecifier 60 | 61 | 62 | FooterText 63 | Generated by CocoaPods - https://cocoapods.org 64 | Title 65 | 66 | Type 67 | PSGroupSpecifier 68 | 69 | 70 | StringsTable 71 | Acknowledgements 72 | Title 73 | Acknowledgements 74 | 75 | 76 | -------------------------------------------------------------------------------- /Pods/Fuzi/Sources/NodeSet.swift: -------------------------------------------------------------------------------- 1 | // NodeSet.swift 2 | // Copyright (c) 2015 Ce Zheng 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | import libxml2 24 | 25 | /// An enumerable set of XML nodes 26 | open class NodeSet: Collection { 27 | // Index type for `Indexable` protocol 28 | public typealias Index = Int 29 | 30 | // IndexDistance type for `Indexable` protocol 31 | public typealias IndexDistance = Int 32 | 33 | fileprivate var cursor = 0 34 | open func next() -> XMLElement? { 35 | defer { 36 | cursor += 1 37 | } 38 | if cursor < self.count { 39 | return self[cursor] 40 | } 41 | return nil 42 | } 43 | 44 | /// Number of nodes 45 | open fileprivate(set) lazy var count: Int = { 46 | return Int(self.cNodeSet?.pointee.nodeNr ?? 0) 47 | }() 48 | 49 | /// First Element 50 | open var first: XMLElement? { 51 | return count > 0 ? self[startIndex] : nil 52 | } 53 | 54 | /// if nodeset is empty 55 | open var isEmpty: Bool { 56 | return (cNodeSet == nil) || (cNodeSet!.pointee.nodeNr == 0) || (cNodeSet!.pointee.nodeTab == nil) 57 | } 58 | 59 | /// Start index 60 | open var startIndex: Index { 61 | return 0 62 | } 63 | 64 | /// End index 65 | open var endIndex: Index { 66 | return count 67 | } 68 | 69 | /** 70 | Get the Nth node from set. 71 | 72 | - parameter idx: node index 73 | 74 | - returns: the idx'th node, nil if out of range 75 | */ 76 | open subscript(_ idx: Index) -> XMLElement { 77 | _precondition(idx >= startIndex && idx < endIndex, "Index of out bound") 78 | return XMLElement(cNode: (cNodeSet!.pointee.nodeTab[idx])!, document: document) 79 | } 80 | 81 | /** 82 | Get the index after `idx` 83 | 84 | - parameter idx: node index 85 | 86 | - returns: the index after `idx` 87 | */ 88 | open func index(after idx: Index) -> Index { 89 | return idx + 1 90 | } 91 | 92 | internal let cNodeSet: xmlNodeSetPtr? 93 | internal let document: XMLDocument! 94 | 95 | internal init(cNodeSet: xmlNodeSetPtr?, document: XMLDocument?) { 96 | self.cNodeSet = cNodeSet 97 | self.document = document 98 | } 99 | } 100 | 101 | /// XPath selector result node set 102 | open class XPathNodeSet: NodeSet { 103 | /// Empty node set 104 | open static let emptySet = XPathNodeSet(cXPath: nil, document: nil) 105 | 106 | fileprivate var cXPath: xmlXPathObjectPtr? 107 | 108 | internal init(cXPath: xmlXPathObjectPtr?, document: XMLDocument?) { 109 | self.cXPath = cXPath 110 | let nodeSet = cXPath?.pointee.nodesetval 111 | super.init(cNodeSet: nodeSet, document: document) 112 | } 113 | 114 | deinit { 115 | if cXPath != nil { 116 | xmlXPathFreeObject(cXPath) 117 | } 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/Logger.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | import Foundation 23 | 24 | extension StandardLibrary { 25 | 26 | /// StandardLibrary.Logger is a tool intended for debugging templates. 27 | /// 28 | /// It logs the rendering of variable and section tags such as `{{name}}` 29 | /// and `{{#name}}...{{/name}}`. 30 | /// 31 | /// To activate logging, add a Logger to the base context of a template: 32 | /// 33 | /// let template = try! Template(string: "{{name}} died at {{age}}.") 34 | /// 35 | /// // Logs all tag renderings with print: 36 | /// let logger = StandardLibrary.Logger() { print($0) } 37 | /// template.extendBaseContext(logger) 38 | /// 39 | /// // Render 40 | /// let data = ["name": "Freddy Mercury", "age": 45] 41 | /// let rendering = try! template.render(data) 42 | /// 43 | /// // Prints: 44 | /// // {{name}} at line 1 did render "Freddy Mercury" as "Freddy Mercury" 45 | /// // {{age}} at line 1 did render 45 as "45" 46 | public final class Logger : MustacheBoxable { 47 | 48 | /// Creates a Logger. 49 | /// 50 | /// - parameter log: A closure that takes a String. Default one logs that 51 | /// string with NSLog(). 52 | public init(_ log: ((String) -> Void)? = nil) { 53 | if let log = log { 54 | self.log = log 55 | } else { 56 | self.log = { NSLog($0) } 57 | } 58 | } 59 | 60 | /// Logger adopts the `MustacheBoxable` protocol so that it can feed 61 | /// Mustache templates. 62 | /// 63 | /// You should not directly call the `mustacheBox` property. 64 | public var mustacheBox: MustacheBox { 65 | return MustacheBox( 66 | willRender: { (tag, box) in 67 | if tag.type == .section { 68 | self.log("\(self.indentationPrefix)\(tag) will render \(box.valueDescription)") 69 | self.indentationLevel += 1 70 | } 71 | return box 72 | }, 73 | didRender: { (tag, box, string) in 74 | if tag.type == .section { 75 | self.indentationLevel -= 1 76 | } 77 | if let string = string { 78 | self.log("\(self.indentationPrefix)\(tag) did render \(box.valueDescription) as \(string.debugDescription)") 79 | } 80 | } 81 | ) 82 | } 83 | 84 | var indentationPrefix: String { 85 | return String(repeating: " ", count: indentationLevel * 2) 86 | } 87 | 88 | fileprivate let log: (String) -> Void 89 | fileprivate var indentationLevel: Int = 0 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/TemplateASTNode.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | enum TemplateASTNode { 27 | case blockNode(Block) // {{$ name }}...{{/ name }} 28 | case partialOverrideNode(PartialOverride) // {{< name }}...{{/ name }} 29 | case partialNode(Partial) // {{> name }} 30 | case sectionNode(Section) // {{# name }}...{{/ name }}, {{^ name }}...{{/ name }} 31 | case textNode(String) // text 32 | case variableNode(Variable) // {{ name }}, {{{ name }}}, {{& name }} 33 | 34 | 35 | // Define structs instead of long tuples 36 | 37 | struct Block { 38 | // {{$ name }}innerTemplateAST{{/ name }} 39 | let innerTemplateAST: TemplateAST 40 | let name: String 41 | } 42 | 43 | struct PartialOverride { 44 | // {{< parentPartial }}childTemplateAST{{/ parentPartial }} 45 | let childTemplateAST: TemplateAST 46 | let parentPartial: Partial 47 | } 48 | 49 | struct Partial { 50 | let templateAST: TemplateAST 51 | let name: String? 52 | } 53 | 54 | struct Section { 55 | let tag: SectionTag 56 | let expression: Expression 57 | let inverted: Bool 58 | } 59 | 60 | struct Variable { 61 | let tag: VariableTag 62 | let expression: Expression 63 | let escapesHTML: Bool 64 | } 65 | 66 | 67 | // Factory methods 68 | 69 | static func block(innerTemplateAST: TemplateAST, name: String) -> TemplateASTNode { 70 | return .blockNode(Block(innerTemplateAST: innerTemplateAST, name: name)) 71 | } 72 | 73 | static func partialOverride(childTemplateAST: TemplateAST, parentTemplateAST: TemplateAST, parentPartialName: String? = nil) -> TemplateASTNode { 74 | return .partialOverrideNode(PartialOverride(childTemplateAST: childTemplateAST, parentPartial: Partial(templateAST: parentTemplateAST, name: parentPartialName))) 75 | } 76 | 77 | static func partial(templateAST: TemplateAST, name: String?) -> TemplateASTNode { 78 | return .partialNode(Partial(templateAST: templateAST, name: name)) 79 | } 80 | 81 | static func section(templateAST: TemplateAST, expression: Expression, inverted: Bool, openingToken: TemplateToken, innerTemplateString: String) -> TemplateASTNode { 82 | let tag = SectionTag(innerTemplateAST: templateAST, openingToken: openingToken, innerTemplateString: innerTemplateString) 83 | return .sectionNode(Section(tag: tag, expression: expression, inverted: inverted)) 84 | } 85 | 86 | static func text(text: String) -> TemplateASTNode { 87 | return .textNode(text) 88 | } 89 | 90 | static func variable(expression: Expression, contentType: ContentType, escapesHTML: Bool, token: TemplateToken) -> TemplateASTNode { 91 | let tag = VariableTag(contentType: contentType, token: token) 92 | return .variableNode(Variable(tag: tag, expression: expression, escapesHTML: escapesHTML)) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Pods/Fuzi/Sources/Helpers.swift: -------------------------------------------------------------------------------- 1 | // Helpers.swift 2 | // Copyright (c) 2015 Ce Zheng 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | import libxml2 24 | 25 | // Public Helpers 26 | 27 | /// For printing an `XMLNode` 28 | extension XMLNode: CustomStringConvertible, CustomDebugStringConvertible { 29 | /// String printed by `print` function 30 | public var description: String { 31 | return self.rawXML 32 | } 33 | 34 | /// String printed by `debugPrint` function 35 | public var debugDescription: String { 36 | return self.rawXML 37 | } 38 | } 39 | 40 | /// For printing an `XMLDocument` 41 | extension XMLDocument: CustomStringConvertible, CustomDebugStringConvertible { 42 | /// String printed by `print` function 43 | public var description: String { 44 | return self.root?.rawXML ?? "" 45 | } 46 | 47 | /// String printed by `debugPrint` function 48 | public var debugDescription: String { 49 | return self.root?.rawXML ?? "" 50 | } 51 | } 52 | 53 | // Internal Helpers 54 | 55 | internal extension String { 56 | subscript (nsrange: NSRange) -> String { 57 | let start = utf16.index(utf16.startIndex, offsetBy: nsrange.location) 58 | let end = utf16.index(start, offsetBy: nsrange.length) 59 | return String(utf16[start.. String cast 64 | 65 | prefix operator ^-^ 66 | internal prefix func ^-^ (ptr: UnsafePointer?) -> String? { 67 | if let ptr = ptr { 68 | return String(validatingUTF8: UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self)) 69 | } 70 | return nil 71 | } 72 | 73 | internal prefix func ^-^ (ptr: UnsafeMutablePointer?) -> String? { 74 | if let ptr = ptr { 75 | return String(validatingUTF8: UnsafeMutableRawPointer(ptr).assumingMemoryBound(to: CChar.self)) 76 | } 77 | return nil 78 | } 79 | 80 | internal struct LinkedCNodes: Sequence, IteratorProtocol { 81 | internal let head: xmlNodePtr? 82 | internal let types: [xmlElementType] 83 | 84 | fileprivate var cursor: xmlNodePtr? 85 | mutating func next() -> xmlNodePtr? { 86 | defer { 87 | if let ptr = cursor { 88 | cursor = ptr.pointee.next 89 | } 90 | } 91 | while let ptr = cursor, !types.contains(where: { $0 == ptr.pointee.type }) { 92 | cursor = ptr.pointee.next 93 | } 94 | return cursor 95 | } 96 | 97 | init(head: xmlNodePtr?, types: [xmlElementType] = [XML_ELEMENT_NODE]) { 98 | self.head = head 99 | self.cursor = head 100 | self.types = types 101 | } 102 | } 103 | 104 | internal func cXMLNode(_ node: xmlNodePtr?, matchesTag tag: String, inNamespace ns: String?) -> Bool { 105 | guard let name = ^-^node?.pointee.name else { 106 | return false 107 | } 108 | var matches = name.compare(tag, options: .caseInsensitive) == .orderedSame 109 | 110 | if let ns = ns { 111 | guard let prefix = ^-^node?.pointee.ns.pointee.prefix else { 112 | return false 113 | } 114 | matches = matches && (prefix.compare(ns, options: .caseInsensitive) == .orderedSame) 115 | } 116 | return matches 117 | } 118 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/EachFilter.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | let EachFilter = Filter { (box: MustacheBox) -> Any? in 27 | 28 | // {{# each(nothing) }}...{{/ }} 29 | if box.isEmpty { 30 | return box 31 | } 32 | 33 | // {{# each(dictionary) }}...{{/ }} 34 | // 35 | // // Renders "firstName: Charles, lastName: Bronson." 36 | // let dictionary = ["firstName": "Charles", "lastName": "Bronson"] 37 | // let template = try! Template(string: "{{# each(dictionary) }}{{ @key }}: {{ . }}{{^ @last }}, {{/ @last }}{{/ each(dictionary) }}.") 38 | // template.register(StandardLibrary.each, forKey: "each") 39 | // try! template.render(["dictionary": dictionary]) 40 | // 41 | // The dictionaryValue box property makes sure to return a 42 | // [String: MustacheBox] whatever the boxed dictionary-like value 43 | // (NSDictionary, [String: Int], [String: CustomObject], etc. 44 | if let dictionary = box.dictionaryValue { 45 | let count = dictionary.count 46 | let customRenderFunctions = dictionary.enumerated().map { (index: Int, element: (key: String, box: MustacheBox)) -> Any? in 47 | let customRenderFunction: RenderFunction = { info in 48 | // Push positional keys in the context stack and then perform 49 | // a regular rendering. 50 | var position: [String: Any] = [:] 51 | position["@index"] = index 52 | position["@indexPlusOne"] = index + 1 53 | position["@indexIsEven"] = (index % 2 == 0) 54 | position["@first"] = (index == 0) 55 | position["@last"] = ((index == count - 1)) 56 | position["@key"] = element.key 57 | 58 | var info = info 59 | info.context = info.context.extendedContext(position) 60 | return try element.box.render(info) 61 | } 62 | return customRenderFunction 63 | } 64 | return customRenderFunctions 65 | } 66 | 67 | 68 | // {{# each(array) }}...{{/ }} 69 | // 70 | // // Renders "1: bread, 2: ham, 3: butter" 71 | // let array = ["bread", "ham", "butter"] 72 | // let template = try! Template(string: "{{# each(array) }}{{ @indexPlusOne }}: {{ . }}{{^ @last }}, {{/ @last }}{{/ each(array) }}.") 73 | // template.register(StandardLibrary.each, forKey: "each") 74 | // try! template.render(["array": array]) 75 | // 76 | // The arrayValue box property makes sure to return a [MustacheBox] whatever 77 | // the boxed collection: NSArray, NSSet, [String], [CustomObject], etc. 78 | if let boxes = box.arrayValue { 79 | let count = boxes.count 80 | let customRenderFunctions = boxes.enumerated().map { (index: Int, box: MustacheBox) -> Any? in 81 | let customRenderFunction: RenderFunction = { info in 82 | // Push positional keys in the context stack and then perform 83 | // a regular rendering. 84 | var position: [String: Any] = [:] 85 | position["@index"] = index 86 | position["@indexPlusOne"] = index + 1 87 | position["@indexIsEven"] = (index % 2 == 0) 88 | position["@first"] = (index == 0) 89 | position["@last"] = ((index == count - 1)) 90 | 91 | var info = info 92 | info.context = info.context.extendedContext(position) 93 | return try box.render(info) 94 | } 95 | return customRenderFunction 96 | } 97 | return customRenderFunctions 98 | } 99 | 100 | // Non-iterable value 101 | throw MustacheError(kind: .renderError, message: "Non-enumerable argument in each filter: \(box.value)") 102 | } 103 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/ObjC/GRMustacheKeyAccess.m: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | #import 25 | #import "GRMustacheKeyAccess.h" 26 | 27 | 28 | static pthread_key_t GRSafeKeysForClassKey; 29 | void freeSafeKeysForClass(void *objects) { 30 | CFRelease((CFMutableDictionaryRef)objects); 31 | } 32 | #define setupSafeKeysForClass() pthread_key_create(&GRSafeKeysForClassKey, freeSafeKeysForClass) 33 | #define getCurrentThreadSafeKeysForClass() (CFMutableDictionaryRef)pthread_getspecific(GRSafeKeysForClassKey) 34 | #define setCurrentThreadSafeKeysForClass(classes) pthread_setspecific(GRSafeKeysForClassKey, classes) 35 | 36 | static Class NSManagedObjectClass; 37 | 38 | 39 | // Plain declarations so that the compiler let us use those selectors 40 | @interface NSObject(GRMustacheCoreDataMethods) 41 | - (NSDictionary *)propertiesByName; 42 | - (id)entity; 43 | @end 44 | 45 | 46 | @implementation GRMustacheKeyAccess 47 | 48 | + (void)initialize 49 | { 50 | NSManagedObjectClass = NSClassFromString(@"NSManagedObject"); 51 | setupSafeKeysForClass(); 52 | } 53 | 54 | + (BOOL)isSafeMustacheKey:(NSString *)key forObject:(id)object 55 | { 56 | NSSet *safeKeys = nil; 57 | { 58 | CFMutableDictionaryRef safeKeysForClass = getCurrentThreadSafeKeysForClass(); 59 | if (!safeKeysForClass) { 60 | safeKeysForClass = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks); 61 | setCurrentThreadSafeKeysForClass(safeKeysForClass); 62 | } 63 | 64 | Class klass = [object class]; 65 | safeKeys = (NSSet *)CFDictionaryGetValue(safeKeysForClass, (__bridge const void *)(klass)); 66 | if (safeKeys == nil) { 67 | NSMutableSet *keys = [self propertyGettersForClass:klass]; 68 | if (NSManagedObjectClass && [object isKindOfClass:NSManagedObjectClass]) { 69 | [keys unionSet:[NSSet setWithArray:[[[object entity] propertiesByName] allKeys]]]; 70 | } 71 | safeKeys = keys; 72 | CFDictionarySetValue(safeKeysForClass, (__bridge const void *)(klass), (__bridge const void *)(safeKeys)); 73 | } 74 | } 75 | 76 | return [safeKeys containsObject:key]; 77 | } 78 | 79 | + (NSMutableSet *)propertyGettersForClass:(Class)klass 80 | { 81 | NSMutableSet *safeKeys = [NSMutableSet set]; 82 | 83 | // Iterate classes up to last super class 84 | 85 | while (klass) { 86 | 87 | // Iterate properties 88 | 89 | unsigned int count; 90 | objc_property_t *properties = class_copyPropertyList(klass, &count); 91 | 92 | for (unsigned int i=0; i! 22 | 23 | var selectedSession: Session? { 24 | return tableView 25 | .indexPathForSelectedRow 26 | .flatMap { sessionsController.record(at: $0) } 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | // Initialize sessionsController with an empty request. 33 | // The request will be updated in updateSearchResults(for:). 34 | let request = SessionWithSnippet.none() 35 | sessionsController = try! FetchedRecordsController(dbQueue, request: request) 36 | 37 | // Update table view as the content of the request changes 38 | // See https://github.com/groue/GRDB.swift#implementing-table-view-updates 39 | sessionsController.trackChanges { [unowned self] _ in 40 | self.tableView.reloadData() 41 | } 42 | 43 | // Fetch sessions and start tracking 44 | try! sessionsController.performFetch() 45 | 46 | // Table view autolayout 47 | tableView.estimatedRowHeight = 62 48 | tableView.rowHeight = UITableViewAutomaticDimension 49 | } 50 | 51 | // MARK: - Table view data source 52 | 53 | override func numberOfSections(in tableView: UITableView) -> Int { 54 | return sessionsController.sections.count 55 | } 56 | 57 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 58 | return sessionsController.sections[section].numberOfRecords 59 | } 60 | 61 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 62 | let cell = tableView.dequeueReusableCell(withIdentifier: "SearchResultTableViewCell", for: indexPath) as! SearchResultTableViewCell 63 | configure(cell, at: indexPath) 64 | return cell 65 | } 66 | 67 | private func configure(_ cell: SearchResultTableViewCell, at indexPath: IndexPath) { 68 | let session = sessionsController.record(at: indexPath) 69 | cell.titleLabel.text = session.title 70 | cell.sessionImageURL = session.imageURL 71 | cell.focusesLabel.text = session.focuses 72 | 73 | // The snippet returned by SQLite wraps matched words in html tags. 74 | // Turn those tags into an NSAttributedString. 75 | let snippet = session.snippet 76 | let font = cell.snippetLabel.font! 77 | let htmlSnippet = "\(snippet)" 78 | if let data = htmlSnippet.data(using: .utf8), 79 | let attributedSnippet = try? NSAttributedString( 80 | data: data, 81 | options: [ 82 | .documentType: NSAttributedString.DocumentType.html, 83 | .characterEncoding: String.Encoding.utf8.rawValue], 84 | documentAttributes: nil) 85 | { 86 | cell.snippetLabel.attributedText = attributedSnippet 87 | } else { 88 | cell.snippetLabel.text = nil 89 | } 90 | } 91 | 92 | // MARK: - Table view delegate 93 | 94 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 95 | // Let the SessionsTableViewController present the session 96 | presentingViewController?.performSegue(withIdentifier: "ShowSession", sender: self) 97 | } 98 | 99 | // MARK: - UISearchResultsUpdating 100 | 101 | /// Part of the UISearchResultsUpdating protocol 102 | func updateSearchResults(for searchController: UISearchController) { 103 | // Turn the user query into a search pattern, and update 104 | // sessionsController's request. 105 | if let queryString = searchController.searchBar.text, 106 | let pattern = FTS5Pattern(matchingAnyTokenIn: queryString) 107 | { 108 | // Valid pattern: full-text search 109 | let sql = "SELECT sessions.*, SNIPPET(fullTextSessions, -1, '', '', '…', 15) AS snippet " + 110 | "FROM sessions " + 111 | "JOIN fullTextSessions ON fullTextSessions.rowid = sessions.rowid AND fullTextSessions MATCH ? " + 112 | "ORDER BY RANK" 113 | try! sessionsController.setRequest(sql: sql, arguments: [pattern]) 114 | } else { 115 | // No pattern: empty the search results 116 | try! sessionsController.setRequest(SessionWithSnippet.none()) 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/TemplateGenerator.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | extension TemplateAST : CustomDebugStringConvertible { 25 | /// A textual representation of `self`, suitable for debugging. 26 | var debugDescription: String { 27 | let string = TemplateGenerator().stringFromTemplateAST(self) 28 | return "TemplateAST(\(string.debugDescription))" 29 | } 30 | } 31 | 32 | extension Template : CustomDebugStringConvertible { 33 | /// A textual representation of `self`, suitable for debugging. 34 | public var debugDescription: String { 35 | let string = TemplateGenerator().stringFromTemplateAST(templateAST) 36 | return "Template(\(string.debugDescription))" 37 | } 38 | } 39 | 40 | final class TemplateGenerator { 41 | let configuration: Configuration 42 | 43 | init(configuration: Configuration? = nil) { 44 | self.configuration = configuration ?? DefaultConfiguration 45 | } 46 | 47 | func stringFromTemplateAST(_ templateAST: TemplateAST) -> String { 48 | buffer = "" 49 | renderTemplateAST(templateAST) 50 | return buffer 51 | } 52 | 53 | fileprivate func renderTemplateAST(_ templateAST: TemplateAST) { 54 | for node in templateAST.nodes { 55 | renderTemplateASTNode(node) 56 | } 57 | } 58 | 59 | func renderTemplateASTNode(_ node: TemplateASTNode) { 60 | switch node { 61 | case .blockNode(let block): 62 | let tagStartDelimiter = configuration.tagDelimiterPair.0 63 | let tagEndDelimiter = configuration.tagDelimiterPair.1 64 | let name = block.name 65 | buffer.append("\(tagStartDelimiter)$\(name)\(tagEndDelimiter)") 66 | renderTemplateAST(block.innerTemplateAST) 67 | buffer.append("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)") 68 | 69 | case .partialOverrideNode(let partialOverride): 70 | let tagStartDelimiter = configuration.tagDelimiterPair.0 71 | let tagEndDelimiter = configuration.tagDelimiterPair.1 72 | let name = partialOverride.parentPartial.name ?? "" 73 | buffer.append("\(tagStartDelimiter)<\(name)\(tagEndDelimiter)") 74 | renderTemplateAST(partialOverride.childTemplateAST) 75 | buffer.append("\(tagStartDelimiter)/\(name)\(tagEndDelimiter)") 76 | 77 | case .partialNode(let partial): 78 | let tagStartDelimiter = configuration.tagDelimiterPair.0 79 | let tagEndDelimiter = configuration.tagDelimiterPair.1 80 | let name = partial.name ?? "" 81 | buffer.append("\(tagStartDelimiter)>\(name)\(tagEndDelimiter)") 82 | 83 | case .sectionNode(let section): 84 | // Change delimiters tags are ignored. Always use configuration tag 85 | // delimiters. 86 | let tagStartDelimiter = configuration.tagDelimiterPair.0 87 | let tagEndDelimiter = configuration.tagDelimiterPair.1 88 | let expression = ExpressionGenerator().stringFromExpression(section.expression) 89 | if section.inverted { 90 | buffer.append("\(tagStartDelimiter)^\(expression)\(tagEndDelimiter)") 91 | } else { 92 | buffer.append("\(tagStartDelimiter)#\(expression)\(tagEndDelimiter)") 93 | } 94 | renderTemplateAST(section.tag.innerTemplateAST) 95 | buffer.append("\(tagStartDelimiter)/\(expression)\(tagEndDelimiter)") 96 | 97 | case .textNode(let text): 98 | buffer.append(text) 99 | 100 | case .variableNode(let variable): 101 | // Change delimiters tags are ignored. Always use configuration tag 102 | // delimiters. 103 | let tagStartDelimiter = configuration.tagDelimiterPair.0 104 | let tagEndDelimiter = configuration.tagDelimiterPair.1 105 | let expression = ExpressionGenerator().stringFromExpression(variable.expression) 106 | if variable.escapesHTML { 107 | buffer.append("\(tagStartDelimiter)\(expression)\(tagEndDelimiter)") 108 | } else if tagStartDelimiter == "{{" && tagEndDelimiter == "}}" { 109 | buffer.append("\(tagStartDelimiter){\(expression)}\(tagEndDelimiter)") 110 | } else { 111 | buffer.append("\(tagStartDelimiter)&\(expression)\(tagEndDelimiter)") 112 | } 113 | } 114 | } 115 | 116 | fileprivate var buffer: String = "" 117 | } 118 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-WWDCCompanion/Pods-WWDCCompanion-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /WWDCCompanion.xcodeproj/xcshareddata/xcschemes/WWDCCompanion.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 58 | 59 | 60 | 62 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 94 | 96 | 102 | 103 | 104 | 105 | 109 | 110 | 111 | 112 | 113 | 114 | 120 | 122 | 128 | 129 | 130 | 131 | 133 | 134 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/Tag.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | // ============================================================================= 27 | // MARK: - TagType 28 | 29 | /// The type of a tag, variable or section. See the documentation of `Tag` for 30 | /// more information. 31 | public enum TagType { 32 | 33 | /// The type of tags such as `{{name}}` and `{{{body}}}`. 34 | case variable 35 | 36 | /// The type of section tags such as `{{#user}}...{{/user}}`. 37 | case section 38 | } 39 | 40 | 41 | // ============================================================================= 42 | // MARK: - Tag 43 | 44 | /// Tag instances represent Mustache tags that render values: 45 | /// 46 | /// - variable tags: `{{name}}` and `{{{body}}}` 47 | /// - section tags: `{{#user}}...{{/user}}` 48 | /// 49 | /// You may meet the Tag class when you implement your own `RenderFunction`, 50 | /// `WillRenderFunction` or `DidRenderFunction`, or filters that perform custom 51 | /// rendering (see `FilterFunction`). 52 | /// 53 | /// - seealso: RenderFunction 54 | /// - seealso: WillRenderFunction 55 | /// - seealso: DidRenderFunction 56 | public protocol Tag: class, CustomStringConvertible { 57 | 58 | // IMPLEMENTATION NOTE 59 | // 60 | // Tag is a class-only protocol so that the Swift compiler does not crash 61 | // when compiling the `tag` property of RenderingInfo. 62 | 63 | /// The type of the tag: variable or section: 64 | /// 65 | /// let render: RenderFunction = { (info: RenderingInfo) in 66 | /// switch info.tag.type { 67 | /// case .variable: 68 | /// return Rendering("variable") 69 | /// case .section: 70 | /// return Rendering("section") 71 | /// } 72 | /// } 73 | /// 74 | /// let template = try! Template(string: "{{object}}, {{#object}}...{{/object}}") 75 | /// 76 | /// // Renders "variable, section" 77 | /// try! template.render(["object": Box(render)]) 78 | var type: TagType { get } 79 | 80 | 81 | /// The literal and unprocessed inner content of the tag. 82 | /// 83 | /// A section tag such as `{{# person }}Hello {{ name }}!{{/ person }}` 84 | /// returns "Hello {{ name }}!". 85 | /// 86 | /// Variable tags such as `{{ name }}` have no inner content: their inner 87 | /// template string is the empty string. 88 | /// 89 | /// // {{# pluralize(count) }}...{{/ }} renders the plural form of the section 90 | /// // content if the `count` argument is greater than 1. 91 | /// let pluralize = Filter { (count: Int?, info: RenderingInfo) in 92 | /// 93 | /// // Pluralize the inner content of the section tag: 94 | /// var string = info.tag.innerTemplateString 95 | /// if let count = count, count > 1 { 96 | /// string += "s" // naive 97 | /// } 98 | /// 99 | /// return Rendering(string) 100 | /// } 101 | /// 102 | /// let template = try! Template(string: "I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.") 103 | /// template.register(pluralize, forKey: "pluralize") 104 | /// 105 | /// // Renders "I have 3 cats." 106 | /// let data = ["cats": ["Kitty", "Pussy", "Melba"]] 107 | /// try! template.render(data) 108 | var innerTemplateString: String { get } 109 | 110 | /// The delimiters of the tag. 111 | var tagDelimiterPair: TagDelimiterPair { get } 112 | 113 | /// Returns the rendering of the tag's inner content. All inner tags are 114 | /// evaluated with the provided context. 115 | /// 116 | /// This method does not return a String, but a Rendering value that wraps 117 | /// both the rendered string and its content type (HTML or Text). 118 | /// 119 | /// The contentType is HTML, unless specified otherwise by `Configuration`, 120 | /// or a `{{% CONTENT_TYPE:TEXT }}` pragma tag. 121 | /// 122 | /// // The strong RenderFunction below wraps a section in a HTML tag. 123 | /// let strong: RenderFunction = { (info: RenderingInfo) -> Rendering in 124 | /// let rendering = try info.tag.render(info.context) 125 | /// return Rendering("\(rendering.string)", .html) 126 | /// } 127 | /// 128 | /// let template = try! Template(string: "{{#strong}}Hello {{name}}{{/strong}}") 129 | /// template.register(strong, forKey: "strong") 130 | /// 131 | /// // Renders "Hello Arthur" 132 | /// try! template.render(["name": Box("Arthur")]) 133 | /// 134 | /// - parameter context: The context stack for evaluating mustache tags. 135 | /// - throws: An eventual rendering error. 136 | /// - returns: The rendering of the tag. 137 | func render(_ context: Context) throws -> Rendering 138 | } 139 | -------------------------------------------------------------------------------- /Pods/Fuzi/Sources/Element.swift: -------------------------------------------------------------------------------- 1 | // Element.swift 2 | // Copyright (c) 2015 Ce Zheng 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | import libxml2 24 | 25 | /// Represents an element in `XMLDocument` or `HTMLDocument` 26 | open class XMLElement: XMLNode { 27 | 28 | /// The element's namespace. 29 | open fileprivate(set) lazy var namespace: String? = { 30 | return ^-^(self.cNode.pointee.ns != nil ?self.cNode.pointee.ns.pointee.prefix :nil) 31 | }() 32 | 33 | /// The element's tag. 34 | open fileprivate(set) lazy var tag: String? = { 35 | return ^-^self.cNode.pointee.name 36 | }() 37 | 38 | // MARK: - Accessing Attributes 39 | /// All attributes for the element. 40 | open fileprivate(set) lazy var attributes: [String : String] = { 41 | var attributes = [String: String]() 42 | var attribute = self.cNode.pointee.properties 43 | while attribute != nil { 44 | if let key = ^-^attribute?.pointee.name, let value = self.attr(key) { 45 | attributes[key] = value 46 | } 47 | attribute = attribute?.pointee.next 48 | } 49 | return attributes 50 | }() 51 | 52 | /** 53 | Returns the value for the attribute with the specified key. 54 | 55 | - parameter name: The attribute name. 56 | - parameter ns: The namespace, or `nil` by default if not using a namespace 57 | 58 | - returns: The attribute value, or `nil` if the attribute is not defined. 59 | */ 60 | open func attr(_ name: String, namespace ns: String? = nil) -> String? { 61 | var value: String? = nil 62 | 63 | let xmlValue: UnsafeMutablePointer? 64 | if let ns = ns { 65 | xmlValue = xmlGetNsProp(cNode, name, ns) 66 | } else { 67 | xmlValue = xmlGetProp(cNode, name) 68 | } 69 | 70 | if let xmlValue = xmlValue { 71 | value = ^-^xmlValue 72 | xmlFree(xmlValue) 73 | } 74 | return value 75 | } 76 | 77 | // MARK: - Accessing Children 78 | 79 | /// The element's children elements. 80 | open var children: [XMLElement] { 81 | return LinkedCNodes(head: cNode.pointee.children).flatMap { 82 | XMLElement(cNode: $0, document: self.document) 83 | } 84 | } 85 | 86 | /** 87 | Get the element's child nodes of specified types 88 | 89 | - parameter types: type of nodes that should be fetched (e.g. .Element, .Text, .Comment) 90 | 91 | - returns: all children of specified types 92 | */ 93 | open func childNodes(ofTypes types: [XMLNodeType]) -> [XMLNode] { 94 | return LinkedCNodes(head: cNode.pointee.children, types: types).flatMap { node in 95 | switch node.pointee.type { 96 | case XMLNodeType.Element: 97 | return XMLElement(cNode: node, document: self.document) 98 | default: 99 | return XMLNode(cNode: node, document: self.document) 100 | } 101 | } 102 | } 103 | 104 | /** 105 | Returns the first child element with a tag, or `nil` if no such element exists. 106 | 107 | - parameter tag: The tag name. 108 | - parameter ns: The namespace, or `nil` by default if not using a namespace 109 | 110 | - returns: The child element. 111 | */ 112 | open func firstChild(tag: String, inNamespace ns: String? = nil) -> XMLElement? { 113 | var nodePtr = cNode.pointee.children 114 | while let cNode = nodePtr { 115 | if cXMLNode(nodePtr, matchesTag: tag, inNamespace: ns) { 116 | return XMLElement(cNode: cNode, document: self.document) 117 | } 118 | nodePtr = cNode.pointee.next 119 | } 120 | return nil 121 | } 122 | 123 | /** 124 | Returns all children elements with the specified tag. 125 | 126 | - parameter tag: The tag name. 127 | - parameter ns: The namepsace, or `nil` by default if not using a namespace 128 | 129 | - returns: The children elements. 130 | */ 131 | open func children(tag: String, inNamespace ns: String? = nil) -> [XMLElement] { 132 | return LinkedCNodes(head: cNode.pointee.children).flatMap { 133 | cXMLNode($0, matchesTag: tag, inNamespace: ns) 134 | ? XMLElement(cNode: $0, document: self.document) : nil 135 | } 136 | } 137 | 138 | // MARK: - Accessing Content 139 | /// Whether the element has a value. 140 | open var isBlank: Bool { 141 | return stringValue.isEmpty 142 | } 143 | 144 | /// A number representation of the element's value, which is generated from the document's `numberFormatter` property. 145 | open fileprivate(set) lazy var numberValue: NSNumber? = { 146 | return self.document.numberFormatter.number(from: self.stringValue) 147 | }() 148 | 149 | /// A date representation of the element's value, which is generated from the document's `dateFormatter` property. 150 | open fileprivate(set) lazy var dateValue: Date? = { 151 | return self.document.dateFormatter.date(from: self.stringValue) 152 | }() 153 | 154 | /** 155 | Returns the child element at the specified index. 156 | 157 | - parameter idx: The index. 158 | 159 | - returns: The child element. 160 | */ 161 | open subscript (idx: Int) -> XMLElement? { 162 | return children[idx] 163 | } 164 | 165 | /** 166 | Returns the value for the attribute with the specified key. 167 | 168 | - parameter name: The attribute name. 169 | 170 | - returns: The attribute value, or `nil` if the attribute is not defined. 171 | */ 172 | open subscript (name: String) -> String? { 173 | return attr(name) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Pods/GRMustache.swift/Sources/JavascriptEscapeHelper.swift: -------------------------------------------------------------------------------- 1 | // The MIT License 2 | // 3 | // Copyright (c) 2015 Gwendal Roué 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | 24 | import Foundation 25 | 26 | final class JavascriptEscapeHelper : MustacheBoxable { 27 | 28 | var mustacheBox: MustacheBox { 29 | // Return a multi-facetted box, because JavascriptEscape interacts in 30 | // various ways with Mustache rendering. 31 | return MustacheBox( 32 | // It has a value: 33 | value: self, 34 | 35 | // JavascriptEscape can be used as a filter: {{ javascriptEscape(x) }}: 36 | filter: Filter(filter), 37 | 38 | // JavascriptEscape escapes all variable tags: {{# javascriptEscape }}...{{ x }}...{{/ javascriptEscape }} 39 | willRender: willRender) 40 | } 41 | 42 | // This function is used for evaluating `javascriptEscape(x)` expressions. 43 | private func filter(_ rendering: Rendering) throws -> Rendering { 44 | return Rendering(JavascriptEscapeHelper.escapeJavascript(rendering.string), rendering.contentType) 45 | } 46 | 47 | // A WillRenderFunction: this function lets JavascriptEscape change values that 48 | // are about to be rendered to their escaped counterpart. 49 | // 50 | // It is activated as soon as the formatter enters the context stack, when 51 | // used in a section {{# javascriptEscape }}...{{/ javascriptEscape }}. 52 | private func willRender(_ tag: Tag, box: MustacheBox) -> Any? { 53 | switch tag.type { 54 | case .variable: 55 | // We don't know if the box contains a String, so let's escape its 56 | // rendering. 57 | return { (info: RenderingInfo) -> Rendering in 58 | let rendering = try box.render(info) 59 | return try self.filter(rendering) 60 | } 61 | case .section: 62 | return box 63 | } 64 | } 65 | 66 | private class func escapeJavascript(_ string: String) -> String { 67 | // This table comes from https://github.com/django/django/commit/8c4a525871df19163d5bfdf5939eff33b544c2e2#django/template/defaultfilters.py 68 | // 69 | // Quoting Malcolm Tredinnick: 70 | // > Added extra robustness to the escapejs filter so that all invalid 71 | // > characters are correctly escaped. This avoids any chance to inject 72 | // > raw HTML inside 84 | /// 85 | /// When used in a section, `javascriptEscape` escapes all inner variable 86 | /// tags in a section: 87 | /// 88 | /// 94 | /// 95 | /// Variable tags buried inside inner sections are escaped as well, so that 96 | /// you can render loop and conditional sections: 97 | /// 98 | /// 104 | /// 105 | /// ### Usage 106 | /// 107 | /// let template = ... 108 | /// template.register(StandardLibrary.javascriptEscape, forKey: "javascriptEscape") 109 | public static let javascriptEscape: MustacheBoxable = JavascriptEscapeHelper() 110 | 111 | /// Iteration is natural to Mustache templates: 112 | /// `{{# users }}{{ name }}, {{/ users }}` renders "Alice, Bob, etc." when the 113 | /// `users` key is given a list of users. 114 | /// 115 | /// The `each` filter gives you some extra keys: 116 | /// 117 | /// - `@index` contains the 0-based index of the item (0, 1, 2, etc.) 118 | /// - `@indexPlusOne` contains the 1-based index of the item (1, 2, 3, etc.) 119 | /// - `@indexIsEven` is true if the 0-based index is even. 120 | /// - `@first` is true for the first item only. 121 | /// - `@last` is true for the last item only. 122 | /// 123 | /// Given the following template: 124 | /// 125 | /// One line per user: 126 | /// {{# each(users) }} 127 | /// - {{ @index }}: {{ name }} 128 | /// {{/}} 129 | /// 130 | /// Comma-separated user names: 131 | /// {{# each(users) }}{{ name }}{{^ @last }}, {{/}}{{/}}. 132 | /// 133 | /// The rendering reads: 134 | /// 135 | /// One line per user: 136 | /// - 0: Alice 137 | /// - 1: Bob 138 | /// - 2: Craig 139 | /// 140 | /// Comma-separated user names: Alice, Bob, Craig. 141 | /// 142 | /// When provided with a dictionary, `each` iterates each key/value pair of the 143 | /// dictionary, stores the key in `@key`, and sets the value as the current 144 | /// context: 145 | /// 146 | /// {{# each(dictionary) }} 147 | /// - {{ @key }}: {{.}} 148 | /// {{/}} 149 | /// 150 | /// Renders: 151 | /// 152 | /// - name: Alice 153 | /// - score: 200 154 | /// - level: 5 155 | /// 156 | /// The other positional keys `@index`, `@first`, etc. are still available when 157 | /// iterating dictionaries. 158 | /// 159 | /// ### Usage 160 | /// 161 | /// let template = ... 162 | /// template.register(StandardLibrary.each, forKey: "each") 163 | public static let each = EachFilter 164 | 165 | /// The zip filter iterates several lists all at once. On each step, one object 166 | /// from each input list enters the rendering context, and makes its own keys 167 | /// available for rendering. 168 | /// 169 | /// Given the Mustache template: 170 | /// 171 | /// {{# zip(users, teams, scores) }} 172 | /// - {{ name }} ({{ team }}): {{ score }} points 173 | /// {{/}} 174 | /// 175 | /// The following JSON input: 176 | /// 177 | /// { 178 | /// "users": [ 179 | /// { "name": "Alice" }, 180 | /// { "name": "Bob" }, 181 | /// ], 182 | /// "teams": [ 183 | /// { "team": "iOS" }, 184 | /// { "team": "Android" }, 185 | /// ], 186 | /// "scores": [ 187 | /// { "score": 100 }, 188 | /// { "score": 200 }, 189 | /// ] 190 | /// } 191 | /// 192 | /// The rendering is: 193 | /// 194 | /// - Alice (iOS): 100 points 195 | /// - Bob (Android): 200 points 196 | /// 197 | /// In the example above, the first step has consumed (Alice, iOS and 100), and 198 | /// the second one (Bob, Android and 200). 199 | /// 200 | /// The zip filter renders a section as many times as there are elements in the 201 | /// longest of its argument: exhausted lists simply do not add anything to the 202 | /// rendering context. 203 | /// 204 | /// ### Usage 205 | /// 206 | /// let template = ... 207 | /// template.register(StandardLibrary.zip, forKey: "zip") 208 | public static let zip = ZipFilter 209 | } 210 | -------------------------------------------------------------------------------- /Pods/Fuzi/README-ja.md: -------------------------------------------------------------------------------- 1 | # Fuzi (斧子) 2 | 3 | [![Build Status](https://api.travis-ci.org/cezheng/Fuzi.svg)](https://travis-ci.org/cezheng/Fuzi) 4 | [![Cocoapods Compatible](https://img.shields.io/cocoapods/v/Fuzi.svg)](https://cocoapods.org/pods/Fuzi) 5 | [![License](https://img.shields.io/cocoapods/l/Fuzi.svg?style=flat&color=gray)](http://opensource.org/licenses/MIT) 6 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![Platform](https://img.shields.io/cocoapods/p/Fuzi.svg?style=flat)](http://cezheng.github.io/Fuzi/) 8 | [![Twitter](https://img.shields.io/badge/twitter-@AdamoCheng-blue.svg?style=flat)](http://twitter.com/AdamoCheng) 9 | 10 | **軽くて、素早くて、 Swift の XML/HTML パーサー。** [[ドキュメント]](http://cezheng.github.io/Fuzi/) 11 | 12 | Fuzi は Mattt Thompson氏の [Ono](https://github.com/mattt/Ono)(斧) に参照し Swift 言語で実装した XML/HTML パーサーである。 13 | 14 | > Fuzi は漢字の`斧子`の中国語発音で、 意味は[Ono](https://github.com/mattt/Ono)(斧)と同じ。Onoは、[Nokogiri](http://nokogiri.org)(鋸)を参照し、創ったもの。 15 | 16 | [English](https://github.com/cezheng/Fuzi/blob/master/README.md) 17 | [简体中文](https://github.com/cezheng/Fuzi/blob/master/README-zh.md) 18 | ## クイックルック 19 | ```swift 20 | let xml = "..." 21 | // or 22 | // let xmlData = 23 | do { 24 | let document = try XMLDocument(string: xml) 25 | // or 26 | // let document = try XMLDocument(data: xmlData) 27 | 28 | if let root = document.root { 29 | // Accessing all child nodes of root element 30 | for element in root.children { 31 | print("\(element.tag): \(element.attributes)") 32 | } 33 | 34 | // Getting child element by tag & accessing attributes 35 | if let length = root.firstChild(tag:"Length", inNamespace: "dc") { 36 | print(length["unit"]) // `unit` attribute 37 | print(length.attributes) // all attributes 38 | } 39 | } 40 | 41 | // XPath & CSS queries 42 | for element in document.xpath("//element") { 43 | print("\(element.tag): \(element.attributes)") 44 | } 45 | 46 | if let firstLink = document.firstChild(css: "a, link") { 47 | print(firstLink["href"]) 48 | } 49 | } catch let error { 50 | print(error) 51 | } 52 | ``` 53 | 54 | ## 機能 55 | ### Onoから貰った機能 56 | - `libxml2`での素早いXMLパース 57 | - [XPath](http://en.wikipedia.org/wiki/XPath) と [CSS](http://en.wikipedia.org/wiki/Cascading_Style_Sheets) クエリ 58 | - 自動的にデータを日付や数字に変換する 59 | - XML ネイムスペース 60 | - `String` や `NSData` や `[CChar]`からXMLDocumentをロードする 61 | - 全面的なユニットテスト 62 | - 100%ドキュメント 63 | 64 | ### Fuziの改善点 65 | - Swift 言語のネーミングやコーディングルールに沿って、クラスやメソッドを再設計した 66 | - 日付や数字変換のフォマットを指定できる 67 | - いくつかのバグ修正 68 | - より多くのHTML便利メソッド 69 | - 全種類のXMLノード取得可能(テキストノードやコメントノードなども含め) 70 | - より多くのCSSクエリ対応 (これから) 71 | 72 | 73 | 74 | ## 環境 75 | 76 | - iOS 8.0+ / Mac OS X 10.9+ 77 | - Xcode 8.0+ 78 | 79 | > Swift 2.3は[0.4.0](../../releases/tag/0.4.0)をご利用ください。 80 | 81 | ## インストール 82 | ### CocoaPodsで 83 | [Cocoapods](http://cocoapods.org/) で簡単に `Fuzi` をインストールできます。 下記のように`Podfile`を編集してください: 84 | 85 | ```ruby 86 | platform :ios, '8.0' 87 | use_frameworks! 88 | 89 | target 'MyApp' do 90 | pod 'Fuzi', '~> 1.0.0' 91 | end 92 | ``` 93 | 94 | そして、下記のコマンドを実行してください: 95 | 96 | ```bash 97 | $ pod install 98 | ``` 99 | 100 | ### 手動で 101 | 1. `Fuzi`フォルダの `*.swift` ファイルをプロジェクトに追加してください。 102 | 2. `libxml2`フォルダをプロジェクトのフォルダのどこか( `/path/to/somewhere`)にコピペしてください。 103 | 3. Xcode プロジェクトの `Build Settings` で: 104 | 1. `Swift Compiler - Search Paths`の`Import Paths`に`/path/to/somewhere/libxml2`を追加してください。 105 | 2. `Search Paths`の`Header Search Paths`に`$(SDKROOT)/usr/include/libxml2`を追加してください。 106 | 3. `Linking`の`Other Linker Flags`に`-lxml2`を追加してください。 107 | 108 | ### Carthageで 109 | プロダクトのディレクトリに`Cartfile` か `Cartfile.private`のファイルを作成し、下記の行を追加してください: 110 | 111 | ``` 112 | github "cezheng/Fuzi" ~> 1.0.0 113 | ``` 114 | そして、下記のコマンドを実行してください: 115 | 116 | ``` 117 | $ carthage update 118 | ``` 119 | 最後に、下記のようにXcodeのtargetを設定してください: 120 | 121 | 1. ビルドターゲットの`General` -> `Embedded Binaries`に、Carthageがビルドした`Fuzi.framework`を追加してください。 122 | 2. `Build Settings`で`Search Paths`の`Header Search Paths`に`$(SDKROOT)/usr/include/libxml2`を追加してください。 123 | 124 | 125 | ##用例 126 | ###XML 127 | ```swift 128 | import Fuzi 129 | 130 | let xml = "..." 131 | do { 132 | // if encoding is omitted, it defaults to NSUTF8StringEncoding 133 | let doc = try XMLDocument(string: html, encoding: NSUTF8StringEncoding) 134 | if let root = document.root { 135 | print(root.tag) 136 | 137 | // define a prefix for a namespace 138 | document.definePrefix("atom", defaultNamespace: "http://www.w3.org/2005/Atom") 139 | 140 | // get first child element with given tag in namespace(optional) 141 | print(root.firstChild(tag: "title", inNamespace: "atom")) 142 | 143 | // iterate through all children 144 | for element in root.children { 145 | print("\(index) \(element.tag): \(element.attributes)") 146 | } 147 | } 148 | // you can also use CSS selector against XMLDocument when you feels it makes sense 149 | } catch let error as XMLError { 150 | switch error { 151 | case .noError: print("wth this should not appear") 152 | case .parserFailure, .invalidData: print(error) 153 | case .libXMLError(let code, let message): 154 | print("libxml error code: \(code), message: \(message)") 155 | } 156 | } 157 | ``` 158 | ###HTML 159 | `HTMLDocument` は `XMLDocument` サブクラス。 160 | 161 | ```swift 162 | import Fuzi 163 | 164 | let html = "..." 165 | do { 166 | // if encoding is omitted, it defaults to NSUTF8StringEncoding 167 | let doc = try HTMLDocument(string: html, encoding: NSUTF8StringEncoding) 168 | 169 | // CSS queries 170 | if let elementById = doc.firstChild(css: "#id") { 171 | print(elementById.stringValue) 172 | } 173 | for link in doc.css("a, link") { 174 | print(link.rawXML) 175 | print(link["href"]) 176 | } 177 | 178 | // XPath queries 179 | if let firstAnchor = doc.firstChild(xpath: "//body/a") { 180 | print(firstAnchor["href"]) 181 | } 182 | for script in doc.xpath("//head/script") { 183 | print(script["src"]) 184 | } 185 | 186 | // Evaluate XPath functions 187 | if let result = doc.eval(xpath: "count(/*/a)") { 188 | print("anchor count : \(result.doubleValue)") 189 | } 190 | 191 | // Convenient HTML methods 192 | print(doc.title) // gets 's innerHTML in <head> 193 | print(doc.head) // gets <head> element 194 | print(doc.body) // gets <body> element 195 | 196 | } catch let error { 197 | print(error) 198 | } 199 | ``` 200 | 201 | ###エラー処理なんて、どうでもいい場合 202 | 203 | ```swift 204 | import Fuzi 205 | 206 | let xml = "..." 207 | 208 | // Don't show me the errors, just don't crash 209 | if let doc1 = try? XMLDocument(string: xml) { 210 | //... 211 | } 212 | 213 | let html = "<html>...</html>" 214 | 215 | // I'm sure this won't crash 216 | let doc2 = try! HTMLDocument(string: html) 217 | //... 218 | ``` 219 | 220 | ###テキストノードを取得したい 221 | テキストノードだけではなく、全種類のノードは取得可能。 222 | 223 | ```swift 224 | let document = ... 225 | // すべてのエレメント、テキストとコメント子要素を取得する 226 | document.root?.childNodes(ofTypes: [.Element, .Text, .Comment]) 227 | 228 | ##Onoからの移行? 229 | 下記2つのサンプルコードを見たら、`Ono`と`Fuzi`の違いをわかる。 230 | 231 | [Onoサンプル](https://github.com/mattt/Ono/blob/master/Example/main.m) 232 | 233 | [Fuziサンプル](FuziDemo/FuziDemo/main.swift) 234 | 235 | ###子要素を取得 236 | **Ono** 237 | 238 | ```objc 239 | [doc firstChildWithTag:tag inNamespace:namespace]; 240 | [doc firstChildWithXPath:xpath]; 241 | [doc firstChildWithXPath:css]; 242 | for (ONOXMLElement *element in parent.children) { 243 | //... 244 | } 245 | [doc childrenWithTag:tag inNamespace:namespace]; 246 | ``` 247 | **Fuzi** 248 | 249 | ```swift 250 | doc.firstChild(tag: tag, inNamespace: namespace) 251 | doc.firstChild(xpath: xpath) 252 | doc.firstChild(css: css) 253 | for element in parent.children { 254 | //... 255 | } 256 | doc.children(tag: tag, inNamespace:namespace) 257 | ``` 258 | ###クエリ結果を読み込む 259 | **Ono** 260 | 261 | Objective-Cの`NSFastEnumeration`。 262 | 263 | ```objc 264 | // simply iterating through the results 265 | // mark `__unused` to unused params `idx` and `stop` 266 | [doc enumerateElementsWithXPath:xpath usingBlock:^(ONOXMLElement *element, __unused NSUInteger idx, __unused BOOL *stop) { 267 | NSLog(@"%@", element); 268 | }]; 269 | 270 | // stop the iteration at second element 271 | [doc enumerateElementsWithXPath:XPath usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) { 272 | *stop = (idx == 1); 273 | }]; 274 | 275 | // getting element by index 276 | ONOXMLDocument *nthElement = [(NSEnumerator*)[doc CSS:css] allObjects][n]; 277 | 278 | // total element count 279 | NSUInteger count = [(NSEnumerator*)[document XPath:xpath] allObjects].count; 280 | ``` 281 | 282 | **Fuzi** 283 | 284 | Swift の `SequenceType` と `Indexable`。 285 | 286 | ```swift 287 | // simply iterating through the results 288 | // no need to write the unused `idx` or `stop` params 289 | for element in doc.xpath(xpath) { 290 | print(element) 291 | } 292 | 293 | // stop the iteration at second element 294 | for (index, element) in doc.xpath(xpath).enumerate() { 295 | if idx == 1 { 296 | break 297 | } 298 | } 299 | 300 | // getting element by index 301 | if let nthElement = doc.css(css)[n] { 302 | //... 303 | } 304 | 305 | // total element count 306 | let count = doc.xpath(xpath).count 307 | ``` 308 | 309 | ###XPath関数を評価する 310 | **Ono** 311 | 312 | ```objc 313 | ONOXPathFunctionResult *result = [doc functionResultByEvaluatingXPath:xpath]; 314 | result.boolValue; //BOOL 315 | result.numericValue; //double 316 | result.stringValue; //NSString 317 | ``` 318 | 319 | **Fuzi** 320 | 321 | ```swift 322 | if let result = doc.eval(xpath: xpath) { 323 | result.boolValue //Bool 324 | result.doubleValue //Double 325 | result.stringValue //String 326 | } 327 | ``` 328 | ## ライセンス 329 | 330 | `Fuzi` のオープンソースライセンスは MIT です。 詳しくはこちら [LICENSE](LICENSE) 。 331 | --------------------------------------------------------------------------------