From f2233cb98461157033255bc945099cb06ec7c3c7 Mon Sep 17 00:00:00 2001 From: Omar Albeik Date: Fri, 21 Aug 2020 14:52:47 +0200 Subject: [PATCH] release: v2.0 (#44) --- .github/workflows/CI.yml | 10 - Package.swift | 36 ++- README.md | 72 +++-- Sources/Identifiable.swift | 33 -- Sources/SingleUserDefaultsStore.swift | 103 ++++++- Sources/UserDefaultsStore.h | 27 -- Sources/UserDefaultsStore.swift | 140 +++++++-- Tests/SingleStoreTests.swift | 93 ++++-- Tests/StoreTests.swift | 127 +++++--- Tests/TestUser.swift | 26 +- UserDefaultsStore.podspec | 12 +- UserDefaultsStore.xcodeproj/project.pbxproj | 28 +- docs/Classes.html | 20 +- docs/Classes/SingleUserDefaultsStore.html | 198 ++++++++++-- .../SingleUserDefaultsStore/Snapshot.html | 284 ++++++++++++++++++ docs/Classes/UserDefaultsStore.html | 240 ++++++++++++--- docs/Classes/UserDefaultsStore/Snapshot.html | 284 ++++++++++++++++++ docs/css/jazzy.css | 7 +- .../Contents/Resources/Documents/Classes.html | 20 +- .../Classes/SingleUserDefaultsStore.html | 198 ++++++++++-- .../SingleUserDefaultsStore/Snapshot.html | 284 ++++++++++++++++++ .../Documents/Classes/UserDefaultsStore.html | 240 ++++++++++++--- .../Classes/UserDefaultsStore/Snapshot.html | 284 ++++++++++++++++++ .../Resources/Documents/css/jazzy.css | 7 +- .../Contents/Resources/Documents/index.html | 84 +++--- .../Contents/Resources/Documents/js/jazzy.js | 13 +- .../Resources/Documents/js/jquery.min.js | 4 +- .../Resources/Documents/js/lunr.min.js | 7 +- .../Documents/js/typeahead.jquery.js | 48 ++- .../Contents/Resources/Documents/search.json | 2 +- .../Contents/Resources/docSet.dsidx | Bin 12288 -> 28672 bytes docs/docsets/UserDefaultsStore.tgz | Bin 74939 -> 79853 bytes docs/index.html | 84 +++--- docs/js/jazzy.js | 13 +- docs/js/jquery.min.js | 4 +- docs/js/lunr.min.js | 7 +- docs/js/typeahead.jquery.js | 48 ++- docs/search.json | 2 +- 38 files changed, 2599 insertions(+), 490 deletions(-) delete mode 100644 Sources/Identifiable.swift delete mode 100644 Sources/UserDefaultsStore.h create mode 100644 docs/Classes/SingleUserDefaultsStore/Snapshot.html create mode 100644 docs/Classes/UserDefaultsStore/Snapshot.html create mode 100644 docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore/Snapshot.html create mode 100644 docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore/Snapshot.html diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2a22e9e..6461b89 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -25,7 +25,6 @@ jobs: run: | brew update brew outdated xctool || brew upgrade xctool - brew install swiftlint - name: Test iOS run: | xcodebuild clean build test -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" | XCPRETTY_JSON_FILE_OUTPUT="xcodebuild-ios.json" xcpretty -f `xcpretty-json-formatter` @@ -57,15 +56,6 @@ jobs: SCHEME: UserDefaultsStore DESTINATION: name=Apple Watch Series 5 - 40mm - Swiftlint: - runs-on: [macos] - name: SwiftLint - steps: - - uses: actions/checkout@v1 - - name: SwiftLint - run: | - brew install swiftlint - swiftlint CocoaPods: name: CocoaPods runs-on: macos-latest diff --git a/Package.swift b/Package.swift index 8c66568..88572d6 100644 --- a/Package.swift +++ b/Package.swift @@ -1,16 +1,44 @@ -// swift-tools-version:5.0 -// The swift-tools-version declares the minimum version of Swift required to build this package. +// swift-tools-version:5.1 +// +// SingleUserDefaultsStore +// +// Copyright (c) 2018-Present Omar Albeik - https://github.com/omaralbeik +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. import PackageDescription let package = Package( name: "UserDefaultsStore", + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + .watchOS(.v6) + ], products: [ .library(name: "UserDefaultsStore", targets: ["UserDefaultsStore"]) ], dependencies: [], targets: [ .target(name: "UserDefaultsStore", dependencies: [], path: "Sources"), - .testTarget(name: "UserDefaultsStoreTests", dependencies: ["UserDefaultsStore"], path: "Tests"), - ] + .testTarget(name: "UserDefaultsStoreTests", dependencies: ["UserDefaultsStore"], path: "Tests") + ], + swiftLanguageVersions: [.v5] ) diff --git a/README.md b/README.md index c5ab789..5425473 100644 --- a/README.md +++ b/README.md @@ -17,27 +17,22 @@ # tl;dr You love Swift's `Codable` protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -**reasonable amount 😅**- of `Codable` objects, in a couple lines of code! +--- -## Installation +## Introducing v2.0 -
-CocoaPods (Recommended) -
-

To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

-
pod 'UserDefaultsStore'
-
+- Removed the `Identifiable` protocol in favor of Swift's `Identifiable`. +- Increased deployment targets to iOS `13.0`, `tvOS 13.0`, `macOS 10.15`, and `watchOS 6.0`. +- Objects defined as non-final classes can now be used as well. +- Added new `generateSnapshot()` and `restoreSnapshot(_:)` methods to generate and restore a `Snapshot` object that can be saved (e.g. to iCloud) and restored later. +- Fixed a bug where `objectsCount` might run out of sync with the actual count of objects in store. -
-Carthage -
-

To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

+--- -
github "omaralbeik/UserDefaultsStore" ~> 1.5.0
-
-
+## Installation
-Swift Package Manager +Swift Package Manager (Recommended)

You can use The Swift Package Manager to install UserDefaultsStore by adding the proper description to your Package.swift file:

@@ -47,7 +42,7 @@ let package = Package( name: "YOUR_PROJECT_NAME", targets: [], dependencies: [ - .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "1.5.0") + .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "2.0.0") ] ) @@ -62,6 +57,23 @@ let package = Package(

Then run swift package update.

+ +
+CocoaPods +
+

To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

+
pod 'UserDefaultsStore'
+
+ +
+Carthage +
+

To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

+ +
github "omaralbeik/UserDefaultsStore" ~> 2.0.0
+
+
+
Manually
@@ -92,39 +104,35 @@ struct Laptop: Codable { Here is how you store them in **UserDefaultsStore**: -### 1. Conform to the `Identifiable` protocol and set the `idKey` property +### 1. Conform to the `Identifiable` protocol and set the `id` property The `Identifiable` protocol lets UserDefaultsStore knows what is the unique id for each object. ```swift struct User: Codable, Identifiable { - static let idKey = \User.id ... } ``` ```swift struct Laptop: Codable, Identifiable { - static let idKey = \Laptop.model + var id: String { model } ... } ``` -> Notice how `User` uses `Int` for its id, while `Laptop` uses `String`, in fact the id can be any `Hashable` type. UserDefaults uses Swift keypaths to refer to properties without actually invoking them. Swift rocks 🤘 - - ### 2. Create UserDefaults Stores ```swift -let usersStore = UserDefaultsStore(uniqueIdentifier: "users")! -let laptopsStore = UserDefaultsStore(uniqueIdentifier: "laptops")! +let usersStore = UserDefaultsStore(uniqueIdentifier: "users") +let laptopsStore = UserDefaultsStore(uniqueIdentifier: "laptops") ``` ### 3. Voilà, you're all set! ```swift let macbook = Laptop(model: "A1278", name: "MacBook Pro") -let john = User(userId: 1, firstName: "John", lastName: "Appleseed", laptop: macbook) +let john = User(id: 1, firstName: "John", lastName: "Appleseed", laptop: macbook) // Save an object to a store try! usersStore.save(john) @@ -156,21 +164,21 @@ laptops.deleteAll() // Know how many objects are stored in a store let usersCount = usersStore.objectsCount -``` +// Create a snapshot +let snapshot = usersStore.generateSnapshot() +// Restore a pre-generated snapshot +try? usersStore.restoreSnapshot(snapshot) +``` ## Looking to store a single item only? Use [`SingleUserDefaultsStore`](https://github.com/omaralbeik/UserDefaultsStore/blob/master/Sources/SingleUserDefaultsStore.swift), it enables storing and retrieving a single value of `Int`, `Double`, `String`, or any `Codable` type. -## Note about using `class` instead of `struct` -At the moment, only `final` classes are supported, please take this into consideration before using the library. - ## Requirements -- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ -- Xcode 10.0+ -- Swift 4.2+ +- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ +- Swift 5.0+ ## Thanks diff --git a/Sources/Identifiable.swift b/Sources/Identifiable.swift deleted file mode 100644 index 985c442..0000000 --- a/Sources/Identifiable.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// UserDefaultsStore -// -// Copyright (c) 2018-Present Omar Albeik - https://github.com/omaralbeik -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -/// Conform to `Identifiable` protocol in uniquely identified objects you want to store in a `UserDefaultsStore`. -public protocol Identifiable { - - /// ID type. - associatedtype ID - - /// Id Key. - static var idKey: WritableKeyPath { get } - -} diff --git a/Sources/SingleUserDefaultsStore.swift b/Sources/SingleUserDefaultsStore.swift index 01d58d1..7c17fb3 100644 --- a/Sources/SingleUserDefaultsStore.swift +++ b/Sources/SingleUserDefaultsStore.swift @@ -24,7 +24,24 @@ import Foundation /// `SingleUserDefaultsStore` offers a convenient way to store a single `Codable` object in `UserDefaults`. -open class SingleUserDefaultsStore { +open class SingleUserDefaultsStore { + /// Used to backup and restore content to store. + public struct Snapshot: Codable { + /// Object. + public let object: Object? + + /// Date when the snapshot was created. + public let dateCreated: Date + + /// Create a new `Snapshot`. + /// - Parameters: + /// - object: Object to include in the snapshot. + /// - dateCreated: Date when the snapshot was created. + public init(object: Object?, dateCreated: Date) { + self.object = object + self.dateCreated = dateCreated + } + } /// Store's unique identifier. /// @@ -38,21 +55,23 @@ open class SingleUserDefaultsStore { open var decoder: JSONDecoder /// UserDefaults store. - private var store: UserDefaults + private let store: UserDefaults - /// Initialize store with given identifier. + /// Initialize store with given identifier. _O(1)_ /// /// **Warning**: Never use the same identifier for two -or more- different stores. /// /// - Parameter uniqueIdentifier: store's unique identifier. /// - Parameter encoder: JSON encoder to be used for encoding object to be stored. _default is `JSONEncoder()`_ /// - Parameter decoder: JSON decoder to be used to decode the stored object. _default is `JSONDecoder()`_ - required public init?( + required public init( uniqueIdentifier: String, encoder: JSONEncoder = .init(), decoder: JSONDecoder = .init() ) { - guard let store = UserDefaults(suiteName: uniqueIdentifier) else { return nil } + guard let store = UserDefaults(suiteName: uniqueIdentifier) else { + fatalError("Can not create a store with identifier: '\(uniqueIdentifier)'.") + } self.uniqueIdentifier = uniqueIdentifier self.encoder = encoder self.decoder = decoder @@ -63,34 +82,85 @@ open class SingleUserDefaultsStore { /// /// - Parameter object: object to save. /// - Throws: JSON encoding error. - public func save(_ object: T) throws { + public func save(_ object: Object) throws { let data = try encoder.encode(generateDict(for: object)) store.set(data, forKey: key) } /// Get object from store. _O(1)_ - public var object: T? { + public var object: Object? { guard let data = store.data(forKey: key) else { return nil } - guard let dict = try? decoder.decode([String: T].self, from: data) else { return nil } + guard let dict = try? decoder.decode([String: Object].self, from: data) else { return nil } return extractObject(from: dict) } /// Delete object from store. _O(1)_ public func delete() { - store.set(nil, forKey: key) + store.removePersistentDomain(forName: uniqueIdentifier) store.removeSuite(named: uniqueIdentifier) } + /// Generate a snapshot that can be saved and restored later. _O(1)_ + /// - Returns: `Snapshot` object representing current contents of the store. + public func generateSnapshot() -> Snapshot { + let now = Date() + store.setValue(now, forKey: lastSnapshotDateKey) + return Snapshot(object: object, dateCreated: now) + } + + /// Restore a pre-generated `Snapshot`. _O(1)_ + /// - Parameter snapshot: `Snapshot` to restore. + /// - Throws: JSON encoding/decoding error. + public func restoreSnapshot(_ snapshot: Snapshot) throws { + let now = Date() + guard let object = snapshot.object else { + delete() + store.setValue(now, forKey: lastRestoreDateKey) + return + } + + let current = self.object + delete() + do { + try save(object) + store.setValue(now, forKey: lastRestoreDateKey) + } catch { + if let current = current { + try save(current) + } + throw error + } + } + + /// Date when the last `Snapshot` was generated. + public var lastSnapshotDate: Date? { + return store.value(forKey: lastSnapshotDateKey) as? Date + } + + /// Date when the last `Snapshot` was successfully restored. + public var lastRestoreDate: Date? { + return store.value(forKey: lastRestoreDateKey) as? Date + } +} + +extension SingleUserDefaultsStore.Snapshot: Equatable where Object: Equatable { + /// Returns a Boolean value indicating whether two snapshots are equal. + /// + /// - Parameters: + /// - lhs: A `Snapshot` object to compare. + /// - rhs: Another `Snapshot` object to compare. + public static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.object == rhs.object && lhs.dateCreated == rhs.dateCreated + } } // MARK: - Helpers private extension SingleUserDefaultsStore { - /// Enclose the object in a dictionary to enable single object storing. /// /// - Parameter object: object. /// - Returns: dictionary enclosing object. - func generateDict(for object: T) -> [String: T] { + func generateDict(for object: Object) -> [String: Object] { return ["object": object] } @@ -98,7 +168,7 @@ private extension SingleUserDefaultsStore { /// /// - Parameter dict: dictionary. /// - Returns: object. - func extractObject(from dict: [String: T]) -> T? { + func extractObject(from dict: [String: Object]) -> Object? { return dict["object"] } @@ -107,4 +177,13 @@ private extension SingleUserDefaultsStore { return "\(uniqueIdentifier)-single-object" } + /// last snapshot date key. + var lastSnapshotDateKey: String { + return "\(uniqueIdentifier)-single-object-last-snapshot-date" + } + + /// last restore date key. + var lastRestoreDateKey: String { + return "\(uniqueIdentifier)-single-object-last-restore-date" + } } diff --git a/Sources/UserDefaultsStore.h b/Sources/UserDefaultsStore.h deleted file mode 100644 index 41c9806..0000000 --- a/Sources/UserDefaultsStore.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// UserDefaultsStore -// -// Copyright (c) 2018-Present Omar Albeik - https://github.com/omaralbeik -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -FOUNDATION_EXPORT double UserDefaultsStoreVersionNumber; -FOUNDATION_EXPORT const unsigned char UserDefaultsStoreVersionString[]; diff --git a/Sources/UserDefaultsStore.swift b/Sources/UserDefaultsStore.swift index dac0aff..95bb23b 100644 --- a/Sources/UserDefaultsStore.swift +++ b/Sources/UserDefaultsStore.swift @@ -24,7 +24,24 @@ import Foundation /// `UserDefaultsStore` offers a convenient way to store a collection of `Codable` objects in `UserDefaults`. -open class UserDefaultsStore { +open class UserDefaultsStore { + /// Used to backup and restore content to store. + public struct Snapshot: Codable { + /// Array of objects. + public let objects: [Object] + + /// Date when the snapshot was created. + public let dateCreated: Date + + /// Create a new `Snapshot`. + /// - Parameters: + /// - object: Array of objects to include in the snapshot. + /// - dateCreated: Date when the snapshot was created. + public init(objects: [Object], dateCreated: Date) { + self.objects = objects + self.dateCreated = dateCreated + } + } /// Store's unique identifier. /// @@ -38,21 +55,23 @@ open class UserDefaultsStore { open var decoder: JSONDecoder /// UserDefaults store. - private var store: UserDefaults + private let store: UserDefaults - /// Initialize store with given identifier. + /// Initialize store with given identifier. _O(1)_ /// /// **Warning**: Never use the same identifier for two -or more- different stores. /// /// - Parameter uniqueIdentifier: store's unique identifier. /// - Parameter encoder: JSON encoder to be used for encoding objects to be stored. _default is `JSONEncoder()`_ /// - Parameter decoder: JSON decoder to be used to decode stored objects. _default is `JSONDecoder()`_ - required public init?( + required public init( uniqueIdentifier: String, encoder: JSONEncoder = .init(), decoder: JSONDecoder = .init() ) { - guard let store = UserDefaults(suiteName: uniqueIdentifier) else { return nil } + guard let store = UserDefaults(suiteName: uniqueIdentifier) else { + fatalError("Can not create a store with identifier: '\(uniqueIdentifier)'.") + } self.uniqueIdentifier = uniqueIdentifier self.encoder = encoder self.decoder = decoder @@ -63,7 +82,7 @@ open class UserDefaultsStore { /// /// - Parameter object: object to save. /// - Throws: JSON encoding error. - public func save(_ object: T) throws { + public func save(_ object: Object) throws { let data = try encoder.encode(object) store.set(data, forKey: key(for: object)) increaseCounter() @@ -73,7 +92,7 @@ open class UserDefaultsStore { /// /// - Parameter optionalObject: optional object to save. /// - Throws: JSON encoding error. - public func save(_ optionalObject: T?) throws { + public func save(_ optionalObject: Object?) throws { guard let object = optionalObject else { return } try save(object) } @@ -82,48 +101,48 @@ open class UserDefaultsStore { /// /// - Parameter objects: object to save. /// - Throws: JSON encoding error. - public func save(_ objects: [T]) throws { + public func save(_ objects: [Object]) throws { let pairs = try objects.map({ (key: key(for: $0), data: try encoder.encode($0)) }) - for pair in pairs { + pairs.forEach { pair in store.set(pair.data, forKey: pair.key) + increaseCounter() } - increaseCounter(by: pairs.count) } /// Get object from store by its id. _O(1)_ /// /// - Parameter id: object id. /// - Returns: optional object. - public func object(withId id: T.ID) -> T? { + public func object(withId id: Object.ID) -> Object? { guard let data = store.data(forKey: key(for: id)) else { return nil } - return try? decoder.decode(T.self, from: data) + return try? decoder.decode(Object.self, from: data) } /// Get array of objects from store for array of m id values. _O(m)_ /// /// - Parameter ids: array of ids. /// - Returns: array of objects with the given ids. - public func objects(withIds ids: [T.ID]) -> [T] { + public func objects(withIds ids: [Object.ID]) -> [Object] { return ids.compactMap { object(withId: $0) } } /// Get all objects from store. _O(n)_ /// /// - Returns: array of all objects in store. - public func allObjects() -> [T] { + public func allObjects() -> [Object] { guard objectsCount > 0 else { return [] } - return store.dictionaryRepresentation().keys.compactMap { key -> T? in + return store.dictionaryRepresentation().keys.compactMap { key -> Object? in guard isObjectKey(key) else { return nil } guard let data = store.data(forKey: key) else { return nil } - return try? decoder.decode(T.self, from: data) + return try? decoder.decode(Object.self, from: data) } } /// Delete object by its id from store. _O(1)_ /// /// - Parameter id: object id. - public func delete(withId id: T.ID) { + public func delete(withId id: Object.ID) { guard hasObject(withId: id) else { return } store.removeObject(forKey: key(for: id)) decreaseCounter() @@ -132,13 +151,14 @@ open class UserDefaultsStore { /// Delete objects with ids from given m ids array. _O(m)_ /// /// - Parameter ids: array of ids. - public func delete(withIds ids: [T.ID]) { + public func delete(withIds ids: [Object.ID]) { ids.forEach { delete(withId: $0) } } /// Delete all objects in store. _O(1)_ public func deleteAll() { store.removePersistentDomain(forName: uniqueIdentifier) + store.removeSuite(named: uniqueIdentifier) } /// Count of all objects in store. _O(1)_ @@ -150,59 +170,116 @@ open class UserDefaultsStore { /// /// - Parameter id: object id to check for. /// - Returns: true if the store has an object with the given id. - public func hasObject(withId id: T.ID) -> Bool { + public func hasObject(withId id: Object.ID) -> Bool { return object(withId: id) != nil } /// Iterate over all objects in store. _O(n)_ /// /// - Parameter object: iteration block. - public func forEach(_ object: (T) -> Void) { + public func forEach(_ object: (Object) -> Void) { allObjects().forEach { object($0) } } + /// Generate a snapshot that can be saved and restored later. _O(n)_ + /// - Returns: `Snapshot` object representing current contents of the store. + public func generateSnapshot() -> Snapshot { + let now = Date() + store.setValue(now, forKey: lastSnapshotDateKey) + return Snapshot(objects: allObjects(), dateCreated: now) + } + + /// Restore a pre-generated `Snapshot`. _O(n)_ + /// - Parameter snapshot: `Snapshot` to restore. + /// - Throws: JSON encoding/decoding error. + public func restoreSnapshot(_ snapshot: Snapshot) throws { + let now = Date() + guard !snapshot.objects.isEmpty else { + deleteAll() + store.setValue(now, forKey: lastRestoreDateKey) + return + } + + let current = allObjects() + deleteAll() + do { + try save(snapshot.objects) + store.setValue(now, forKey: lastRestoreDateKey) + } catch { + try save(current) + throw error + } + } + + /// Date when the last `Snapshot` was generated. + public var lastSnapshotDate: Date? { + return store.value(forKey: lastSnapshotDateKey) as? Date + } + + /// Date when the last `Snapshot` was successfully restored. + public var lastRestoreDate: Date? { + return store.value(forKey: lastRestoreDateKey) as? Date + } +} + +extension UserDefaultsStore.Snapshot: Equatable where Object: Equatable { + /// Returns a Boolean value indicating whether two snapshots are equal. + /// + /// - Parameters: + /// - lhs: A `Snapshot` object to compare. + /// - rhs: Another `Snapshot` object to compare. + public static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.objects == rhs.objects && lhs.dateCreated == rhs.dateCreated + } } // MARK: - Helpers private extension UserDefaultsStore { - /// Increase objects count counter. - func increaseCounter(by count: Int = 1) { + func increaseCounter() { let currentCount = store.integer(forKey: counterKey) - store.set(currentCount + count, forKey: counterKey) + store.set(currentCount + 1, forKey: counterKey) } /// Decrease objects count counter. - func decreaseCounter(by count: Int = 1) { + func decreaseCounter() { let currentCount = store.integer(forKey: counterKey) guard currentCount > 0 else { return } - guard currentCount - count >= 0 else { return } - store.set(currentCount - count, forKey: counterKey) + guard currentCount - 1 >= 0 else { return } + store.set(currentCount - 1, forKey: counterKey) } - } // MARK: - Keys private extension UserDefaultsStore { - /// counter key. var counterKey: String { return "\(uniqueIdentifier)-count" } + /// last snapshot date key. + var lastSnapshotDateKey: String { + return "\(uniqueIdentifier)-last-snapshot-date" + } + + /// last restore date key. + var lastRestoreDateKey: String { + return "\(uniqueIdentifier)-last-restore-date" + } + /// store key for object. /// /// - Parameter object: object. /// - Returns: UserDefaults key for given object. - func key(for object: T) -> String { - return "\(uniqueIdentifier)-\(object[keyPath: T.idKey])" + func key(for object: Object) -> String { + return "\(uniqueIdentifier)-\(object.id)" } /// store key for object by its id. /// /// - Parameter id: object id. /// - Returns: UserDefaults key for given id. - func key(for id: T.ID) -> String { + func key(for id: Object.ID) -> String { return "\(uniqueIdentifier)-\(id)" } @@ -213,5 +290,4 @@ private extension UserDefaultsStore { func isObjectKey(_ key: String) -> Bool { return key.starts(with: "\(uniqueIdentifier)-") } - } diff --git a/Tests/SingleStoreTests.swift b/Tests/SingleStoreTests.swift index 9782129..9b065af 100644 --- a/Tests/SingleStoreTests.swift +++ b/Tests/SingleStoreTests.swift @@ -25,29 +25,20 @@ import XCTest @testable import UserDefaultsStore final class SingleStoreTests: XCTestCase { - - func testCreateStore() { - let store = createFreshUsersStore() - XCTAssertNotNil(store) - } + private typealias Snapshot = SingleUserDefaultsStore.Snapshot func testCreateStoreWithCustomEncoderAndDecoder() { let encoder = JSONEncoder() let decoder = JSONDecoder() - let store = createFreshUsersStore(encoder: encoder, decoder: decoder) + let store = createFreshUserStore(encoder: encoder, decoder: decoder) XCTAssertNotNil(store) - XCTAssert(store?.encoder === encoder) - XCTAssert(store?.decoder === decoder) - } - - func testCreateInvalidStore() { - let invalidStore = SingleUserDefaultsStore(uniqueIdentifier: UserDefaults.globalDomain) - XCTAssertNil(invalidStore) + XCTAssert(store.encoder === encoder) + XCTAssert(store.decoder === decoder) } func testSaveObject() { - let store = createFreshUsersStore()! + let store = createFreshUserStore() XCTAssertNoThrow(try store.save(TestUser.john)) XCTAssertNotNil(store.object) @@ -55,32 +46,96 @@ final class SingleStoreTests: XCTestCase { } func testSaveInvalidObject() { - let store = createFreshUsersStore()! + let store = createFreshUserStore() XCTAssertThrowsError(try store.save(TestUser.invalid)) } func testObject() { - let store = createFreshUsersStore()! + let store = createFreshUserStore() XCTAssertNoThrow(try store.save(TestUser.johnson)) XCTAssertNotNil(store.object) } + func testGenerateSnapshot() { + let store = createFreshUserStore() + + XCTAssertNoThrow(try store.save(TestUser.john)) + + XCTAssertNil(store.lastSnapshotDate) + + let snapshot = store.generateSnapshot() + + XCTAssertEqual(snapshot.object, TestUser.john) + XCTAssertNotNil(store.lastSnapshotDate) + XCTAssertEqual(store.lastSnapshotDate, snapshot.dateCreated) + } + + func testRestoreSnapshot() { + var store = createFreshUserStore() + + XCTAssertNoThrow(try store.save(TestUser.john)) + + let snapshot = store.generateSnapshot() + + store = createFreshUserStore() + + XCTAssertNil(store.lastRestoreDate) + XCTAssertNoThrow(try store.restoreSnapshot(snapshot)) + XCTAssertNotNil(store.lastRestoreDate) + XCTAssertEqual(store.object, TestUser.john) + } + + func testRestoreEmptySnapshot() { + let store = createFreshUserStore() + + XCTAssertNoThrow(try store.save(TestUser.john)) + + let snapshot = SingleUserDefaultsStore.Snapshot(object: nil, dateCreated: Date()) + + XCTAssertNoThrow(try store.restoreSnapshot(snapshot)) + + XCTAssertNotNil(store.lastRestoreDate) + XCTAssertNil(store.object) + } + + func testRestoreSnapshotWithInvalidObjects() { + let store = createFreshUserStore() + + XCTAssertNoThrow(try store.save(TestUser.john)) + + let snapshot = Snapshot(object: TestUser.invalid, dateCreated: Date()) + + XCTAssertThrowsError(try store.restoreSnapshot(snapshot)) + XCTAssertNil(store.lastRestoreDate) + XCTAssertEqual(store.object, TestUser.john) + } + + func testSnapshotEquality() { + let now = Date() + let snapshot1 = Snapshot(object: TestUser.john, dateCreated: now) + let snapshot2 = Snapshot(object: TestUser.john, dateCreated: now) + XCTAssertEqual(snapshot1, snapshot2) + + let snapshot3 = Snapshot(object: TestUser.james, dateCreated: Date()) + let snapshot4 = Snapshot(object: TestUser.john, dateCreated: Date()) + XCTAssertNotEqual(snapshot3, snapshot4) + } } // MARK: - Helpers private extension SingleStoreTests { - func createFreshUsersStore( + func createFreshUserStore( encoder: JSONEncoder = .init(), decoder: JSONDecoder = .init() - ) -> SingleUserDefaultsStore? { + ) -> SingleUserDefaultsStore { let store = SingleUserDefaultsStore( uniqueIdentifier: "single-user", encoder: encoder, decoder: decoder ) - store?.delete() + store.delete() return store } diff --git a/Tests/StoreTests.swift b/Tests/StoreTests.swift index 162c63a..238e8a0 100644 --- a/Tests/StoreTests.swift +++ b/Tests/StoreTests.swift @@ -25,11 +25,7 @@ import XCTest @testable import UserDefaultsStore final class StoreTests: XCTestCase { - - func testCreateStore() { - let store = createFreshUsersStore() - XCTAssertNotNil(store) - } + private typealias Snapshot = UserDefaultsStore.Snapshot func testCreateStoreWithCustomEncoderAndDecoder() { let encoder = JSONEncoder() @@ -37,8 +33,8 @@ final class StoreTests: XCTestCase { let store = createFreshUsersStore(encoder: encoder, decoder: decoder) XCTAssertNotNil(store) - XCTAssert(store?.encoder === encoder) - XCTAssert(store?.decoder === decoder) + XCTAssert(store.encoder === encoder) + XCTAssert(store.decoder === decoder) } func testCreateStoreWithCustomDecoder() { @@ -51,17 +47,12 @@ final class StoreTests: XCTestCase { let store = createFreshUsersStore(encoder: encoder, decoder: decoder) XCTAssertNotNil(store) - XCTAssertNoThrow(try store?.save(TestUser.john)) - XCTAssertEqual(store?.object(withId: TestUser.john.userId), TestUser.john) - } - - func testCreateInvalidStore() { - let invalidStore = UserDefaultsStore(uniqueIdentifier: UserDefaults.globalDomain) - XCTAssertNil(invalidStore) + XCTAssertNoThrow(try store.save(TestUser.john)) + XCTAssertEqual(store.object(withId: TestUser.john.id), TestUser.john) } func testSaveObject() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.john)) XCTAssertEqual(store.objectsCount, 1) @@ -69,7 +60,7 @@ final class StoreTests: XCTestCase { } func testSaveOptional() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(nil)) XCTAssertEqual(store.objectsCount, 0) @@ -80,7 +71,7 @@ final class StoreTests: XCTestCase { } func testSaveInvalidObject() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() let optionalUser: TestUser? = TestUser.invalid XCTAssertThrowsError(try store.save(TestUser.invalid)) @@ -88,7 +79,7 @@ final class StoreTests: XCTestCase { } func testSaveObjects() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save([TestUser.john, TestUser.johnson, TestUser.james])) XCTAssertEqual(store.objectsCount, 3) @@ -98,7 +89,7 @@ final class StoreTests: XCTestCase { } func testSaveInvalidObjects() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertThrowsError(try store.save([TestUser.james, TestUser.john, TestUser.invalid])) XCTAssertEqual(store.objectsCount, 0) @@ -106,7 +97,7 @@ final class StoreTests: XCTestCase { } func testObject() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.johnson)) let user = store.object(withId: 2) @@ -117,19 +108,19 @@ final class StoreTests: XCTestCase { } func testObjects() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.james)) XCTAssertNoThrow(try store.save(TestUser.johnson)) - let users = store.objects(withIds: [TestUser.james.userId, TestUser.johnson.userId, 5]) + let users = store.objects(withIds: [TestUser.james.id, TestUser.johnson.id, 5]) XCTAssertEqual(users.count, 2) XCTAssert(users.contains(TestUser.james)) XCTAssert(users.contains(TestUser.johnson)) } func testDeleteObject() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.james)) @@ -140,19 +131,19 @@ final class StoreTests: XCTestCase { } func testDeleteObjects() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.james)) XCTAssertNoThrow(try store.save(TestUser.johnson)) XCTAssertEqual(store.objectsCount, 2) - store.delete(withIds: [TestUser.james.userId, 5, 6, 8]) + store.delete(withIds: [TestUser.james.id, 5, 6, 8]) XCTAssertEqual(store.objectsCount, 1) } func testGetAll() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.john)) XCTAssertEqual(store.allObjects(), [TestUser.john]) @@ -168,7 +159,7 @@ final class StoreTests: XCTestCase { } func testDeleteAll() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertNoThrow(try store.save(TestUser.john)) XCTAssertNoThrow(try store.save(TestUser.johnson)) @@ -179,15 +170,15 @@ final class StoreTests: XCTestCase { } func testHasObject() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() XCTAssertFalse(store.hasObject(withId: 10)) XCTAssertNoThrow(try store.save(TestUser.john)) - XCTAssert(store.hasObject(withId: TestUser.john.userId)) + XCTAssert(store.hasObject(withId: TestUser.john.id)) } func testForEach() { - let store = createFreshUsersStore()! + let store = createFreshUsersStore() let users = [TestUser.john, TestUser.johnson, TestUser.james] XCTAssertNoThrow(try store.save(users)) @@ -200,22 +191,90 @@ final class StoreTests: XCTestCase { XCTAssertEqual(counter, 3) } + func testGenerateSnapshot() { + let store = createFreshUsersStore() + + let users = [TestUser.john, TestUser.johnson].sorted() + XCTAssertNoThrow(try store.save(users)) + + XCTAssertNil(store.lastSnapshotDate) + + let snapshot = store.generateSnapshot() + + XCTAssertEqual(snapshot.objects.sorted(), users) + XCTAssertNotNil(store.lastSnapshotDate) + XCTAssertEqual(store.lastSnapshotDate, snapshot.dateCreated) + } + + func testRestoreSnapshot() { + var store = createFreshUsersStore() + + let users = [TestUser.john, TestUser.johnson].sorted() + XCTAssertNoThrow(try store.save(users)) + + let snapshot = store.generateSnapshot() + + store = createFreshUsersStore() + + XCTAssertNil(store.lastRestoreDate) + XCTAssertNoThrow(try store.restoreSnapshot(snapshot)) + XCTAssertNotNil(store.lastRestoreDate) + XCTAssertEqual(store.allObjects().sorted(), users) + } + + func testRestoreEmptySnapshot() { + let store = createFreshUsersStore() + + let users = [TestUser.john, TestUser.johnson] + XCTAssertNoThrow(try store.save(users)) + + let snapshot = Snapshot(objects: [], dateCreated: Date()) + + XCTAssertNoThrow(try store.restoreSnapshot(snapshot)) + + XCTAssertNotNil(store.lastRestoreDate) + XCTAssertEqual(store.objectsCount, 0) + } + + func testRestoreSnapshotWithInvalidObjects() { + let store = createFreshUsersStore() + + let users = [TestUser.john, TestUser.johnson] + XCTAssertNoThrow(try store.save(users)) + + let snapshot = Snapshot(objects: [TestUser.invalid], dateCreated: Date()) + + XCTAssertThrowsError(try store.restoreSnapshot(snapshot)) + XCTAssertNil(store.lastRestoreDate) + XCTAssertEqual(store.objectsCount, users.count) + XCTAssert(store.hasObject(withId: TestUser.john.id)) + XCTAssert(store.hasObject(withId: TestUser.johnson.id)) + } + + func testSnapshotEquality() { + let now = Date() + let snapshot1 = Snapshot(objects: [TestUser.john], dateCreated: now) + let snapshot2 = Snapshot(objects: [TestUser.john], dateCreated: now) + XCTAssertEqual(snapshot1, snapshot2) + + let snapshot3 = Snapshot(objects: [TestUser.john], dateCreated: Date()) + let snapshot4 = Snapshot(objects: [TestUser.john], dateCreated: Date().addingTimeInterval(1)) + XCTAssertNotEqual(snapshot3, snapshot4) + } } // MARK: - Helpers private extension StoreTests { - func createFreshUsersStore( encoder: JSONEncoder = .init(), decoder: JSONDecoder = .init() - ) -> UserDefaultsStore? { + ) -> UserDefaultsStore { let store = UserDefaultsStore( uniqueIdentifier: "users", encoder: encoder, decoder: decoder ) - store?.deleteAll() + store.deleteAll() return store } - } diff --git a/Tests/TestUser.swift b/Tests/TestUser.swift index 8b2d6dd..078ea45 100644 --- a/Tests/TestUser.swift +++ b/Tests/TestUser.swift @@ -23,26 +23,28 @@ @testable import UserDefaultsStore -struct TestUser: Codable, Equatable, CustomStringConvertible, Identifiable { - static let idKey = \TestUser.userId - - var userId: Int - var firstName: String - var lastName: String - var age: Double +struct TestUser: Codable, Equatable, Comparable, CustomStringConvertible, Identifiable { + let id: Int + let firstName: String + let lastName: String + let age: Double static func == (lhs: TestUser, rhs: TestUser) -> Bool { - return lhs.userId == rhs.userId + return lhs.id == rhs.id + } + + static func < (lhs: TestUser, rhs: TestUser) -> Bool { + lhs.id < rhs.id } var description: String { return firstName } - static let john = TestUser(userId: 1, firstName: "John", lastName: "Appleseed", age: 21.5) - static let johnson = TestUser(userId: 2, firstName: "Johnson", lastName: "Smith", age: 26.3) - static let james = TestUser(userId: 3, firstName: "James", lastName: "Robert", age: 14) + static let john = TestUser(id: 1, firstName: "John", lastName: "Appleseed", age: 21.5) + static let johnson = TestUser(id: 2, firstName: "Johnson", lastName: "Smith", age: 26.3) + static let james = TestUser(id: 3, firstName: "James", lastName: "Robert", age: 14) - static let invalid = TestUser(userId: 4, firstName: "", lastName: "", age: .nan) + static let invalid = TestUser(id: 4, firstName: "", lastName: "", age: .nan) } diff --git a/UserDefaultsStore.podspec b/UserDefaultsStore.podspec index d81959a..4b7c203 100644 --- a/UserDefaultsStore.podspec +++ b/UserDefaultsStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "UserDefaultsStore" - s.version = "1.5.0" + s.version = "2.0.0" s.summary = "Why not use UserDefaults to store Codable objects 😉" s.description = <<-DESC You love Swift"s Codable protocol and use it everywhere, here is an easy and very light way to store - reasonable amount 😅 - of Codable objects, in a couple lines of code. @@ -15,11 +15,11 @@ Pod::Spec.new do |s| s.module_name = "UserDefaultsStore" s.source = { :git => "https://github.com/omaralbeik/UserDefaultsStore.git", :tag => s.version } s.source_files = "Sources/**/*.swift" - s.swift_version = "5.0" + s.swift_versions = ['5.1', '5.2'] s.requires_arc = true - s.ios.deployment_target = "8.0" - s.osx.deployment_target = "10.10" - s.tvos.deployment_target = "9.0" - s.watchos.deployment_target = "2.0" + s.ios.deployment_target = "13.0" + s.osx.deployment_target = "10.15" + s.tvos.deployment_target = "13.0" + s.watchos.deployment_target = "6.0" end diff --git a/UserDefaultsStore.xcodeproj/project.pbxproj b/UserDefaultsStore.xcodeproj/project.pbxproj index e3583c7..fef488a 100644 --- a/UserDefaultsStore.xcodeproj/project.pbxproj +++ b/UserDefaultsStore.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 07563822210A396C005CDE89 /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0756381E210A396C005CDE89 /* Identifiable.swift */; }; 07563831210A3C4A005CDE89 /* UserDefaultsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0756381D210A396C005CDE89 /* UserDefaultsStore.swift */; }; 07563832210A3C4D005CDE89 /* StoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07563825210A3975005CDE89 /* StoreTests.swift */; }; 078D2A692147BA7500923390 /* SingleUserDefaultsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 078D2A682147BA7500923390 /* SingleUserDefaultsStore.swift */; }; @@ -27,8 +26,6 @@ /* Begin PBXFileReference section */ 0756381D210A396C005CDE89 /* UserDefaultsStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsStore.swift; sourceTree = ""; }; - 0756381E210A396C005CDE89 /* Identifiable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Identifiable.swift; sourceTree = ""; }; - 0756381F210A396C005CDE89 /* UserDefaultsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserDefaultsStore.h; sourceTree = ""; }; 07563825210A3975005CDE89 /* StoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreTests.swift; sourceTree = ""; }; 07563827210A39D8005CDE89 /* UserDefaultsStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UserDefaultsStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0756382B210A3A59005CDE89 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -61,10 +58,8 @@ isa = PBXGroup; children = ( 0756382B210A3A59005CDE89 /* Info.plist */, - 0756381E210A396C005CDE89 /* Identifiable.swift */, - 0756381D210A396C005CDE89 /* UserDefaultsStore.swift */, 078D2A682147BA7500923390 /* SingleUserDefaultsStore.swift */, - 0756381F210A396C005CDE89 /* UserDefaultsStore.h */, + 0756381D210A396C005CDE89 /* UserDefaultsStore.swift */, ); path = Sources; sourceTree = ""; @@ -235,7 +230,6 @@ files = ( 07563831210A3C4A005CDE89 /* UserDefaultsStore.swift in Sources */, 078D2A692147BA7500923390 /* SingleUserDefaultsStore.swift in Sources */, - 07563822210A396C005CDE89 /* Identifiable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -398,13 +392,17 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.5; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = com.omaralbeik.UserDefaultsStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Debug; }; @@ -422,13 +420,17 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.5; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = com.omaralbeik.UserDefaultsStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Release; }; @@ -442,13 +444,17 @@ COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = com.omaralbeik.UserDefaultsStoreTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Debug; }; @@ -462,12 +468,16 @@ COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = com.omaralbeik.UserDefaultsStoreTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Release; }; diff --git a/docs/Classes.html b/docs/Classes.html index 9ecf327..937eafe 100644 --- a/docs/Classes.html +++ b/docs/Classes.html @@ -21,7 +21,7 @@

- UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

@@ -50,16 +50,14 @@ + - - - @@ -100,7 +98,7 @@

Classes

Declaration

Swift

-
open class SingleUserDefaultsStore<T> where T : Decodable, T : Encodable
+
open class SingleUserDefaultsStore<Object> where Object : Decodable, Object : Encodable
@@ -128,7 +126,7 @@

Declaration

Declaration

Swift

-
open class UserDefaultsStore<T> where T : Decodable, T : Encodable, T : Identifiable
+
open class UserDefaultsStore<Object> where Object : Decodable, Object : Encodable, Object : Identifiable
@@ -143,8 +141,8 @@

Declaration

diff --git a/docs/Classes/SingleUserDefaultsStore.html b/docs/Classes/SingleUserDefaultsStore.html index 27abf15..2f5ac11 100644 --- a/docs/Classes/SingleUserDefaultsStore.html +++ b/docs/Classes/SingleUserDefaultsStore.html @@ -21,7 +21,7 @@

- UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

@@ -50,16 +50,14 @@ + - - - @@ -72,7 +70,8 @@

SingleUserDefaultsStore

-
open class SingleUserDefaultsStore<T> where T : Decodable, T : Encodable
+ +
open class SingleUserDefaultsStore<Object> where Object : Decodable, Object : Encodable
@@ -85,6 +84,35 @@

SingleUserDefaultsStore

    +
  • +
    + + + + Snapshot + +
    +
    +
    +
    +
    +
    +

    Used to backup and restore content to store.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Snapshot : Codable
    +
    extension SingleUserDefaultsStore.Snapshot: Equatable where Object: Equatable
    + +
    +
    +
    +
    +
  • @@ -171,9 +199,9 @@

    Declaration

  • @@ -181,7 +209,7 @@

    Declaration

    -

    Initialize store with given identifier.

    +

    Initialize store with given identifier. O(1)

    Warning: Never use the same identifier for two -or more- different stores.

    @@ -190,7 +218,7 @@

    Declaration

    Declaration

    Swift

    -
    required public init?(
    +                          
    required public init(
         uniqueIdentifier: String,
         encoder: JSONEncoder = .init(),
         decoder: JSONDecoder = .init()
    @@ -249,7 +277,7 @@ 

    Parameters

    - save(_:) + save(_:)
    @@ -269,7 +297,7 @@

    Parameters

    Declaration

    Swift

    -
    public func save(_ object: T) throws
    +
    public func save(_ object: Object) throws
    @@ -315,7 +343,7 @@

    Parameters

    Declaration

    Swift

    -
    public var object: T? { get }
    +
    public var object: Object? { get }
    @@ -349,6 +377,142 @@

    Declaration

  • +
  • +
    + + + + generateSnapshot() + +
    +
    +
    +
    +
    +
    +

    Generate a snapshot that can be saved and restored later. O(1)

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func generateSnapshot() -> Snapshot
    + +
    +
    +
    +

    Return Value

    +

    Snapshot object representing current contents of the store.

    +
    +
    +
    +
  • +
  • +
    + + + + restoreSnapshot(_:) + +
    +
    +
    +
    +
    +
    +

    Restore a pre-generated Snapshot. O(1)

    +
    +

    Throws

    + JSON encoding/decoding error. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func restoreSnapshot(_ snapshot: Snapshot) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + snapshot + + +
    +

    Snapshot to restore.

    +
    +
    +
    +
    +
    +
  • +
  • +
    + + + + lastSnapshotDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was generated.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastSnapshotDate: Date? { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + lastRestoreDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was successfully restored.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastRestoreDate: Date? { get }
    + +
    +
    +
    +
    +
@@ -357,8 +521,8 @@

Declaration

diff --git a/docs/Classes/SingleUserDefaultsStore/Snapshot.html b/docs/Classes/SingleUserDefaultsStore/Snapshot.html new file mode 100644 index 0000000..0fb49a7 --- /dev/null +++ b/docs/Classes/SingleUserDefaultsStore/Snapshot.html @@ -0,0 +1,284 @@ + + + + Snapshot Structure Reference + + + + + + + + + + + + + + + + +
+

+ + UserDefaultsStore 2.0.0 Docs + + (100% documented) +

+ +

+

+ +
+

+ + +
+ + + +
+ +
+ +
+
+

Snapshot

+
+
+ +
public struct Snapshot : Codable
+
extension SingleUserDefaultsStore.Snapshot: Equatable where Object: Equatable
+ +
+
+

Used to backup and restore content to store.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + object + +
    +
    +
    +
    +
    +
    +

    Object.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let object: Object?
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + dateCreated + +
    +
    +
    +
    +
    +
    +

    Date when the snapshot was created.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let dateCreated: Date
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a new Snapshot.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(object: Object?, dateCreated: Date)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + object + + +
    +

    Object to include in the snapshot.

    +
    +
    + + dateCreated + + +
    +

    Date when the snapshot was created.

    +
    +
    +
    +
    +
    +
  • +
+
+
+
+ + +
+ +

Available where Object: Equatable

+

+
+
+
    +
  • +
    + + + + ==(_:_:) + +
    +
    +
    +
    +
    +
    +

    Returns a Boolean value indicating whether two snapshots are equal.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func == (lhs: `Self`, rhs: `Self`) -> Bool
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + lhs + + +
    +

    A Snapshot object to compare.

    +
    +
    + + rhs + + +
    +

    Another Snapshot object to compare.

    +
    +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/Classes/UserDefaultsStore.html b/docs/Classes/UserDefaultsStore.html index 0ef678d..e3e655c 100644 --- a/docs/Classes/UserDefaultsStore.html +++ b/docs/Classes/UserDefaultsStore.html @@ -21,7 +21,7 @@

- UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

@@ -50,16 +50,14 @@ + - - - @@ -72,7 +70,8 @@

UserDefaultsStore

-
open class UserDefaultsStore<T> where T : Decodable, T : Encodable, T : Identifiable
+ +
open class UserDefaultsStore<Object> where Object : Decodable, Object : Encodable, Object : Identifiable
@@ -85,6 +84,35 @@

UserDefaultsStore

    +
  • +
    + + + + Snapshot + +
    +
    +
    +
    +
    +
    +

    Used to backup and restore content to store.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Snapshot : Codable
    +
    extension UserDefaultsStore.Snapshot: Equatable where Object: Equatable
    + +
    +
    +
    +
    +
  • @@ -171,9 +199,9 @@

    Declaration

  • @@ -181,7 +209,7 @@

    Declaration

    -

    Initialize store with given identifier.

    +

    Initialize store with given identifier. O(1)

    Warning: Never use the same identifier for two -or more- different stores.

    @@ -190,7 +218,7 @@

    Declaration

    Declaration

    Swift

    -
    required public init?(
    +                          
    required public init(
         uniqueIdentifier: String,
         encoder: JSONEncoder = .init(),
         decoder: JSONDecoder = .init()
    @@ -249,7 +277,7 @@ 

    Parameters

    - save(_:) + save(_:)
    @@ -269,7 +297,7 @@

    Parameters

    Declaration

    Swift

    -
    public func save(_ object: T) throws
    +
    public func save(_ object: Object) throws
    @@ -300,7 +328,7 @@

    Parameters

    - save(_:) + save(_:)
    @@ -320,7 +348,7 @@

    Parameters

    Declaration

    Swift

    -
    public func save(_ optionalObject: T?) throws
    +
    public func save(_ optionalObject: Object?) throws
    @@ -351,7 +379,7 @@

    Parameters

    - save(_:) + save(_:)
    @@ -371,7 +399,7 @@

    Parameters

    Declaration

    Swift

    -
    public func save(_ objects: [T]) throws
    +
    public func save(_ objects: [Object]) throws
    @@ -400,9 +428,9 @@

    Parameters

  • @@ -417,7 +445,7 @@

    Parameters

    Declaration

    Swift

    -
    public func object(withId id: T.ID) -> T?
    +
    public func object(withId id: Object.ID) -> Object?
    @@ -450,9 +478,9 @@

    Return Value

  • @@ -467,7 +495,7 @@

    Return Value

    Declaration

    Swift

    -
    public func objects(withIds ids: [T.ID]) -> [T]
    +
    public func objects(withIds ids: [Object.ID]) -> [Object]
    @@ -517,7 +545,7 @@

    Return Value

    Declaration

    Swift

    -
    public func allObjects() -> [T]
    +
    public func allObjects() -> [Object]
@@ -531,9 +559,9 @@

Return Value

  • @@ -548,7 +576,7 @@

    Return Value

    Declaration

    Swift

    -
    public func delete(withId id: T.ID)
    +
    public func delete(withId id: Object.ID)
    @@ -577,9 +605,9 @@

    Parameters

  • @@ -594,7 +622,7 @@

    Parameters

    Declaration

    Swift

    -
    public func delete(withIds ids: [T.ID])
    +
    public func delete(withIds ids: [Object.ID])
    @@ -677,9 +705,9 @@

    Declaration

  • @@ -694,7 +722,7 @@

    Declaration

    Declaration

    Swift

    -
    public func hasObject(withId id: T.ID) -> Bool
    +
    public func hasObject(withId id: Object.ID) -> Bool
    @@ -729,7 +757,7 @@

    Return Value

    - forEach(_:) + forEach(_:)
  • @@ -744,7 +772,7 @@

    Return Value

    Declaration

    Swift

    -
    public func forEach(_ object: (T) -> Void)
    +
    public func forEach(_ object: (Object) -> Void)
    @@ -770,6 +798,142 @@

    Parameters

    +
  • +
    + + + + generateSnapshot() + +
    +
    +
    +
    +
    +
    +

    Generate a snapshot that can be saved and restored later. O(n)

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func generateSnapshot() -> Snapshot
    + +
    +
    +
    +

    Return Value

    +

    Snapshot object representing current contents of the store.

    +
    +
    +
    +
  • +
  • +
    + + + + restoreSnapshot(_:) + +
    +
    +
    +
    +
    +
    +

    Restore a pre-generated Snapshot. O(n)

    +
    +

    Throws

    + JSON encoding/decoding error. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func restoreSnapshot(_ snapshot: Snapshot) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + snapshot + + +
    +

    Snapshot to restore.

    +
    +
    +
    +
    +
    +
  • +
  • +
    + + + + lastSnapshotDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was generated.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastSnapshotDate: Date? { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + lastRestoreDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was successfully restored.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastRestoreDate: Date? { get }
    + +
    +
    +
    +
    +
  • @@ -778,8 +942,8 @@

    Parameters

    diff --git a/docs/Classes/UserDefaultsStore/Snapshot.html b/docs/Classes/UserDefaultsStore/Snapshot.html new file mode 100644 index 0000000..2092249 --- /dev/null +++ b/docs/Classes/UserDefaultsStore/Snapshot.html @@ -0,0 +1,284 @@ + + + + Snapshot Structure Reference + + + + + + + + + + + + + + + + +
    +

    + + UserDefaultsStore 2.0.0 Docs + + (100% documented) +

    + +

    +

    + +
    +

    + + +
    + + + +
    + +
    + +
    +
    +

    Snapshot

    +
    +
    + +
    public struct Snapshot : Codable
    +
    extension UserDefaultsStore.Snapshot: Equatable where Object: Equatable
    + +
    +
    +

    Used to backup and restore content to store.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + objects + +
      +
      +
      +
      +
      +
      +

      Array of objects.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let objects: [Object]
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + dateCreated + +
      +
      +
      +
      +
      +
      +

      Date when the snapshot was created.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let dateCreated: Date
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Create a new Snapshot.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(objects: [Object], dateCreated: Date)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + object + + +
      +

      Array of objects to include in the snapshot.

      +
      +
      + + dateCreated + + +
      +

      Date when the snapshot was created.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + + +
    + +

    Available where Object: Equatable

    +

    +
    +
    +
      +
    • +
      + + + + ==(_:_:) + +
      +
      +
      +
      +
      +
      +

      Returns a Boolean value indicating whether two snapshots are equal.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static func == (lhs: `Self`, rhs: `Self`) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + lhs + + +
      +

      A Snapshot object to compare.

      +
      +
      + + rhs + + +
      +

      Another Snapshot object to compare.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/css/jazzy.css b/docs/css/jazzy.css index 3ca6b31..ff59f5f 100644 --- a/docs/css/jazzy.css +++ b/docs/css/jazzy.css @@ -111,7 +111,6 @@ pre code { @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } - .header { display: flex; padding: 8px; @@ -157,7 +156,6 @@ pre code { word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } - .nav-groups { list-style-type: none; padding-left: 0; } @@ -188,7 +186,6 @@ pre code { order: 2; flex: 1; padding-bottom: 60px; } } - .section { padding: 0 32px; border-bottom: 1px solid #ddd; } @@ -248,8 +245,10 @@ pre code { content: ""; display: block; } .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; padding-left: 3px; - margin-left: 0px; + margin-left: 20px; font-size: 1rem; } .item .declaration-note { font-size: .85em; diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes.html index 9ecf327..937eafe 100644 --- a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes.html +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes.html @@ -21,7 +21,7 @@

    - UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

    @@ -50,16 +50,14 @@ + - - - @@ -100,7 +98,7 @@

    Classes

    Declaration

    Swift

    -
    open class SingleUserDefaultsStore<T> where T : Decodable, T : Encodable
    +
    open class SingleUserDefaultsStore<Object> where Object : Decodable, Object : Encodable
    @@ -128,7 +126,7 @@

    Declaration

    Declaration

    Swift

    -
    open class UserDefaultsStore<T> where T : Decodable, T : Encodable, T : Identifiable
    +
    open class UserDefaultsStore<Object> where Object : Decodable, Object : Encodable, Object : Identifiable
    @@ -143,8 +141,8 @@

    Declaration

    diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore.html index 27abf15..2f5ac11 100644 --- a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore.html +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore.html @@ -21,7 +21,7 @@

    - UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

    @@ -50,16 +50,14 @@ + - - - @@ -72,7 +70,8 @@

    SingleUserDefaultsStore

    -
    open class SingleUserDefaultsStore<T> where T : Decodable, T : Encodable
    + +
    open class SingleUserDefaultsStore<Object> where Object : Decodable, Object : Encodable
    @@ -85,6 +84,35 @@

    SingleUserDefaultsStore

      +
    • +
      + + + + Snapshot + +
      +
      +
      +
      +
      +
      +

      Used to backup and restore content to store.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Snapshot : Codable
      +
      extension SingleUserDefaultsStore.Snapshot: Equatable where Object: Equatable
      + +
      +
      +
      +
      +
    • @@ -171,9 +199,9 @@

      Declaration

    • @@ -181,7 +209,7 @@

      Declaration

      -

      Initialize store with given identifier.

      +

      Initialize store with given identifier. O(1)

      Warning: Never use the same identifier for two -or more- different stores.

      @@ -190,7 +218,7 @@

      Declaration

      Declaration

      Swift

      -
      required public init?(
      +                          
      required public init(
           uniqueIdentifier: String,
           encoder: JSONEncoder = .init(),
           decoder: JSONDecoder = .init()
      @@ -249,7 +277,7 @@ 

      Parameters

      - save(_:) + save(_:)
      @@ -269,7 +297,7 @@

      Parameters

      Declaration

      Swift

      -
      public func save(_ object: T) throws
      +
      public func save(_ object: Object) throws
      @@ -315,7 +343,7 @@

      Parameters

      Declaration

      Swift

      -
      public var object: T? { get }
      +
      public var object: Object? { get }
      @@ -349,6 +377,142 @@

      Declaration

    • +
    • +
      + + + + generateSnapshot() + +
      +
      +
      +
      +
      +
      +

      Generate a snapshot that can be saved and restored later. O(1)

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func generateSnapshot() -> Snapshot
      + +
      +
      +
      +

      Return Value

      +

      Snapshot object representing current contents of the store.

      +
      +
      +
      +
    • +
    • +
      + + + + restoreSnapshot(_:) + +
      +
      +
      +
      +
      +
      +

      Restore a pre-generated Snapshot. O(1)

      +
      +

      Throws

      + JSON encoding/decoding error. + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func restoreSnapshot(_ snapshot: Snapshot) throws
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + snapshot + + +
      +

      Snapshot to restore.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + lastSnapshotDate + +
      +
      +
      +
      +
      +
      +

      Date when the last Snapshot was generated.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var lastSnapshotDate: Date? { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + lastRestoreDate + +
      +
      +
      +
      +
      +
      +

      Date when the last Snapshot was successfully restored.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var lastRestoreDate: Date? { get }
      + +
      +
      +
      +
      +
    @@ -357,8 +521,8 @@

    Declaration

    diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore/Snapshot.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore/Snapshot.html new file mode 100644 index 0000000..0fb49a7 --- /dev/null +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/SingleUserDefaultsStore/Snapshot.html @@ -0,0 +1,284 @@ + + + + Snapshot Structure Reference + + + + + + + + + + + + + + + + +
    +

    + + UserDefaultsStore 2.0.0 Docs + + (100% documented) +

    + +

    +

    + +
    +

    + + +
    + + + +
    + +
    + +
    +
    +

    Snapshot

    +
    +
    + +
    public struct Snapshot : Codable
    +
    extension SingleUserDefaultsStore.Snapshot: Equatable where Object: Equatable
    + +
    +
    +

    Used to backup and restore content to store.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + object + +
      +
      +
      +
      +
      +
      +

      Object.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let object: Object?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + dateCreated + +
      +
      +
      +
      +
      +
      +

      Date when the snapshot was created.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let dateCreated: Date
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Create a new Snapshot.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(object: Object?, dateCreated: Date)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + object + + +
      +

      Object to include in the snapshot.

      +
      +
      + + dateCreated + + +
      +

      Date when the snapshot was created.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + + +
    + +

    Available where Object: Equatable

    +

    +
    +
    +
      +
    • +
      + + + + ==(_:_:) + +
      +
      +
      +
      +
      +
      +

      Returns a Boolean value indicating whether two snapshots are equal.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static func == (lhs: `Self`, rhs: `Self`) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + lhs + + +
      +

      A Snapshot object to compare.

      +
      +
      + + rhs + + +
      +

      Another Snapshot object to compare.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore.html index 0ef678d..e3e655c 100644 --- a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore.html +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore.html @@ -21,7 +21,7 @@

    - UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

    @@ -50,16 +50,14 @@ + - - - @@ -72,7 +70,8 @@

    UserDefaultsStore

    -
    open class UserDefaultsStore<T> where T : Decodable, T : Encodable, T : Identifiable
    + +
    open class UserDefaultsStore<Object> where Object : Decodable, Object : Encodable, Object : Identifiable
    @@ -85,6 +84,35 @@

    UserDefaultsStore

      +
    • +
      + + + + Snapshot + +
      +
      +
      +
      +
      +
      +

      Used to backup and restore content to store.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Snapshot : Codable
      +
      extension UserDefaultsStore.Snapshot: Equatable where Object: Equatable
      + +
      +
      +
      +
      +
    • @@ -171,9 +199,9 @@

      Declaration

    • @@ -181,7 +209,7 @@

      Declaration

      -

      Initialize store with given identifier.

      +

      Initialize store with given identifier. O(1)

      Warning: Never use the same identifier for two -or more- different stores.

      @@ -190,7 +218,7 @@

      Declaration

      Declaration

      Swift

      -
      required public init?(
      +                          
      required public init(
           uniqueIdentifier: String,
           encoder: JSONEncoder = .init(),
           decoder: JSONDecoder = .init()
      @@ -249,7 +277,7 @@ 

      Parameters

      - save(_:) + save(_:)
      @@ -269,7 +297,7 @@

      Parameters

      Declaration

      Swift

      -
      public func save(_ object: T) throws
      +
      public func save(_ object: Object) throws
      @@ -300,7 +328,7 @@

      Parameters

      - save(_:) + save(_:)
      @@ -320,7 +348,7 @@

      Parameters

      Declaration

      Swift

      -
      public func save(_ optionalObject: T?) throws
      +
      public func save(_ optionalObject: Object?) throws
      @@ -351,7 +379,7 @@

      Parameters

      - save(_:) + save(_:)
      @@ -371,7 +399,7 @@

      Parameters

      Declaration

      Swift

      -
      public func save(_ objects: [T]) throws
      +
      public func save(_ objects: [Object]) throws
      @@ -400,9 +428,9 @@

      Parameters

    • @@ -417,7 +445,7 @@

      Parameters

      Declaration

      Swift

      -
      public func object(withId id: T.ID) -> T?
      +
      public func object(withId id: Object.ID) -> Object?
      @@ -450,9 +478,9 @@

      Return Value

    • @@ -467,7 +495,7 @@

      Return Value

      Declaration

      Swift

      -
      public func objects(withIds ids: [T.ID]) -> [T]
      +
      public func objects(withIds ids: [Object.ID]) -> [Object]
      @@ -517,7 +545,7 @@

      Return Value

      Declaration

      Swift

      -
      public func allObjects() -> [T]
      +
      public func allObjects() -> [Object]
    @@ -531,9 +559,9 @@

    Return Value

  • @@ -548,7 +576,7 @@

    Return Value

    Declaration

    Swift

    -
    public func delete(withId id: T.ID)
    +
    public func delete(withId id: Object.ID)
    @@ -577,9 +605,9 @@

    Parameters

  • @@ -594,7 +622,7 @@

    Parameters

    Declaration

    Swift

    -
    public func delete(withIds ids: [T.ID])
    +
    public func delete(withIds ids: [Object.ID])
    @@ -677,9 +705,9 @@

    Declaration

  • @@ -694,7 +722,7 @@

    Declaration

    Declaration

    Swift

    -
    public func hasObject(withId id: T.ID) -> Bool
    +
    public func hasObject(withId id: Object.ID) -> Bool
    @@ -729,7 +757,7 @@

    Return Value

    - forEach(_:) + forEach(_:)
  • @@ -744,7 +772,7 @@

    Return Value

    Declaration

    Swift

    -
    public func forEach(_ object: (T) -> Void)
    +
    public func forEach(_ object: (Object) -> Void)
    @@ -770,6 +798,142 @@

    Parameters

    +
  • +
    + + + + generateSnapshot() + +
    +
    +
    +
    +
    +
    +

    Generate a snapshot that can be saved and restored later. O(n)

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func generateSnapshot() -> Snapshot
    + +
    +
    +
    +

    Return Value

    +

    Snapshot object representing current contents of the store.

    +
    +
    +
    +
  • +
  • +
    + + + + restoreSnapshot(_:) + +
    +
    +
    +
    +
    +
    +

    Restore a pre-generated Snapshot. O(n)

    +
    +

    Throws

    + JSON encoding/decoding error. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func restoreSnapshot(_ snapshot: Snapshot) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + snapshot + + +
    +

    Snapshot to restore.

    +
    +
    +
    +
    +
    +
  • +
  • +
    + + + + lastSnapshotDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was generated.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastSnapshotDate: Date? { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + lastRestoreDate + +
    +
    +
    +
    +
    +
    +

    Date when the last Snapshot was successfully restored.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var lastRestoreDate: Date? { get }
    + +
    +
    +
    +
    +
  • @@ -778,8 +942,8 @@

    Parameters

    diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore/Snapshot.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore/Snapshot.html new file mode 100644 index 0000000..2092249 --- /dev/null +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/Classes/UserDefaultsStore/Snapshot.html @@ -0,0 +1,284 @@ + + + + Snapshot Structure Reference + + + + + + + + + + + + + + + + +
    +

    + + UserDefaultsStore 2.0.0 Docs + + (100% documented) +

    + +

    +

    + +
    +

    + + +
    + + + +
    + +
    + +
    +
    +

    Snapshot

    +
    +
    + +
    public struct Snapshot : Codable
    +
    extension UserDefaultsStore.Snapshot: Equatable where Object: Equatable
    + +
    +
    +

    Used to backup and restore content to store.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + objects + +
      +
      +
      +
      +
      +
      +

      Array of objects.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let objects: [Object]
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + dateCreated + +
      +
      +
      +
      +
      +
      +

      Date when the snapshot was created.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let dateCreated: Date
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Create a new Snapshot.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(objects: [Object], dateCreated: Date)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + object + + +
      +

      Array of objects to include in the snapshot.

      +
      +
      + + dateCreated + + +
      +

      Date when the snapshot was created.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + + +
    + +

    Available where Object: Equatable

    +

    +
    +
    +
      +
    • +
      + + + + ==(_:_:) + +
      +
      +
      +
      +
      +
      +

      Returns a Boolean value indicating whether two snapshots are equal.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static func == (lhs: `Self`, rhs: `Self`) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + lhs + + +
      +

      A Snapshot object to compare.

      +
      +
      + + rhs + + +
      +

      Another Snapshot object to compare.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/css/jazzy.css b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/css/jazzy.css index 3ca6b31..ff59f5f 100644 --- a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/css/jazzy.css +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/css/jazzy.css @@ -111,7 +111,6 @@ pre code { @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } - .header { display: flex; padding: 8px; @@ -157,7 +156,6 @@ pre code { word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } - .nav-groups { list-style-type: none; padding-left: 0; } @@ -188,7 +186,6 @@ pre code { order: 2; flex: 1; padding-bottom: 60px; } } - .section { padding: 0 32px; border-bottom: 1px solid #ddd; } @@ -248,8 +245,10 @@ pre code { content: ""; display: block; } .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; padding-left: 3px; - margin-left: 0px; + margin-left: 20px; font-size: 1rem; } .item .declaration-note { font-size: .85em; diff --git a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/index.html b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/index.html index 2f5672c..e4c77c7 100644 --- a/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/index.html +++ b/docs/docsets/UserDefaultsStore.docset/Contents/Resources/Documents/index.html @@ -20,7 +20,7 @@

    - UserDefaultsStore Docs + UserDefaultsStore 2.0.0 Docs (100% documented)

    @@ -49,16 +49,14 @@ + - - - @@ -74,9 +72,8 @@

    - Build Status + Build Status Test Coverage - codebeat badge Platforms Cocoapods Carthage compatible @@ -88,26 +85,23 @@

    tl;dr

    You love Swift’s Codable protocol and use it everywhere, who doesn’t! Here is an easy and very light way to store and retrieve -reasonable amount 😅- of Codable objects, in a couple lines of code!

    -

    Installation

    -
    -CocoaPods (Recommended) -
    -

    To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

    -
    pod 'UserDefaultsStore'
    -
    +
    +

    Introducing v2.0

    -
    -Carthage -
    -

    To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

    +
      +
    • Removed the Identifiable protocol in favor of Swift’s Identifiable.
    • +
    • Increased deployment targets to iOS 13.0, tvOS 13.0, macOS 10.15, and watchOS 6.0.
    • +
    • Objects defined as non-final classes can now be used as well.
    • +
    • Added new generateSnapshot() and restoreSnapshot(_:) methods to generate and restore a Snapshot object that can be saved (e.g. to iCloud) and restored later.
    • +
    • Fixed a bug where objectsCount might run out of sync with the actual count of objects in store.
    • +
    -
    github "omaralbeik/UserDefaultsStore" ~> 1.5.0
    -
    -
    +
    +

    Installation

    -Swift Package Manager +Swift Package Manager (Recommended)

    You can use The Swift Package Manager to install UserDefaultsStore by adding the proper description to your Package.swift file:

    @@ -117,7 +111,7 @@

    Installation

    name: "YOUR_PROJECT_NAME", targets: [], dependencies: [ - .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "1.5.0") + .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "2.0.0") ] )
    @@ -132,6 +126,22 @@

    Installation

    Then run swift package update.

    +
    +CocoaPods +
    +

    To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

    +
    pod 'UserDefaultsStore'
    +
    + +
    +Carthage +
    +

    To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

    + +
    github "omaralbeik/UserDefaultsStore" ~> 2.0.0
    +
    +
    +
    Manually
    @@ -154,30 +164,25 @@

    Usage

    Here is how you store them in UserDefaultsStore:

    -

    1. Conform to the Identifiable protocol and set the idKey property

    +

    1. Conform to the Identifiable protocol and set the id property

    -

    The Identifiable protocol lets UserDefaultsStore knows what is the unique id for each object.

    +

    The Identifiable protocol lets UserDefaultsStore knows what is the unique id for each object.

    struct User: Codable, Identifiable {
    -    static let idKey = \User.id
         ...
     }
     
    struct Laptop: Codable, Identifiable {
    -    static let idKey = \Laptop.model
    +    var id: String { model }
         ...
     }
     
    - -
    -

    Notice how User uses Int for its id, while Laptop uses String, in fact the id can be any Hashable type. UserDefaults uses Swift keypaths to refer to properties without actually invoking them. Swift rocks 🤘

    -

    2. Create UserDefaults Stores

    -
    let usersStore = UserDefaultsStore<User>(uniqueIdentifier: "users")!
    -let laptopsStore = UserDefaultsStore<Laptop>(uniqueIdentifier: "laptops")!
    +
    let usersStore = UserDefaultsStore<User>(uniqueIdentifier: "users")
    +let laptopsStore = UserDefaultsStore<Laptop>(uniqueIdentifier: "laptops")
     

    3. Voilà, you’re all set!

    let macbook = Laptop(model: "A1278", name: "MacBook Pro")
    -let john = User(userId: 1, firstName: "John", lastName: "Appleseed", laptop: macbook)
    +let john = User(id: 1, firstName: "John", lastName: "Appleseed", laptop: macbook)
     
     // Save an object to a store
     try! usersStore.save(john)
    @@ -213,9 +218,6 @@ 

    3. Voilà, you’re all

    Looking to store a single item only?

    Use SingleUserDefaultsStore, it enables storing and retrieving a single value of Int, Double, String, or any Codable type.

    -

    Note about using class instead of struct

    - -

    At the moment, only final classes are supported, please take this into consideration before using the library.

    Requirements

    - - @@ -74,9 +72,8 @@

    - Build Status + Build Status Test Coverage - codebeat badge Platforms Cocoapods Carthage compatible @@ -88,26 +85,23 @@

    tl;dr

    You love Swift’s Codable protocol and use it everywhere, who doesn’t! Here is an easy and very light way to store and retrieve -reasonable amount 😅- of Codable objects, in a couple lines of code!

    -

    Installation

    -
    -CocoaPods (Recommended) -
    -

    To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

    -
    pod 'UserDefaultsStore'
    -
    +
    +

    Introducing v2.0

    -
    -Carthage -
    -

    To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

    +
      +
    • Removed the Identifiable protocol in favor of Swift’s Identifiable.
    • +
    • Increased deployment targets to iOS 13.0, tvOS 13.0, macOS 10.15, and watchOS 6.0.
    • +
    • Objects defined as non-final classes can now be used as well.
    • +
    • Added new generateSnapshot() and restoreSnapshot(_:) methods to generate and restore a Snapshot object that can be saved (e.g. to iCloud) and restored later.
    • +
    • Fixed a bug where objectsCount might run out of sync with the actual count of objects in store.
    • +
    -
    github "omaralbeik/UserDefaultsStore" ~> 1.5.0
    -
    -
    +
    +

    Installation

    -Swift Package Manager +Swift Package Manager (Recommended)

    You can use The Swift Package Manager to install UserDefaultsStore by adding the proper description to your Package.swift file:

    @@ -117,7 +111,7 @@

    Installation

    name: "YOUR_PROJECT_NAME", targets: [], dependencies: [ - .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "1.5.0") + .package(url: "https://github.com/omaralbeik/UserDefaultsStore.git", from: "2.0.0") ] )
    @@ -132,6 +126,22 @@

    Installation

    Then run swift package update.

    +
    +CocoaPods +
    +

    To integrate UserDefaultsStore into your Xcode project using CocoaPods, specify it in your Podfile:

    +
    pod 'UserDefaultsStore'
    +
    + +
    +Carthage +
    +

    To integrate UserDefaultsStore into your Xcode project using Carthage, specify it in your Cartfile:

    + +
    github "omaralbeik/UserDefaultsStore" ~> 2.0.0
    +
    +
    +
    Manually
    @@ -154,30 +164,25 @@

    Usage

    Here is how you store them in UserDefaultsStore:

    -

    1. Conform to the Identifiable protocol and set the idKey property

    +

    1. Conform to the Identifiable protocol and set the id property

    -

    The Identifiable protocol lets UserDefaultsStore knows what is the unique id for each object.

    +

    The Identifiable protocol lets UserDefaultsStore knows what is the unique id for each object.

    struct User: Codable, Identifiable {
    -    static let idKey = \User.id
         ...
     }
     
    struct Laptop: Codable, Identifiable {
    -    static let idKey = \Laptop.model
    +    var id: String { model }
         ...
     }
     
    - -
    -

    Notice how User uses Int for its id, while Laptop uses String, in fact the id can be any Hashable type. UserDefaults uses Swift keypaths to refer to properties without actually invoking them. Swift rocks 🤘

    -

    2. Create UserDefaults Stores

    -
    let usersStore = UserDefaultsStore<User>(uniqueIdentifier: "users")!
    -let laptopsStore = UserDefaultsStore<Laptop>(uniqueIdentifier: "laptops")!
    +
    let usersStore = UserDefaultsStore<User>(uniqueIdentifier: "users")
    +let laptopsStore = UserDefaultsStore<Laptop>(uniqueIdentifier: "laptops")
     

    3. Voilà, you’re all set!

    let macbook = Laptop(model: "A1278", name: "MacBook Pro")
    -let john = User(userId: 1, firstName: "John", lastName: "Appleseed", laptop: macbook)
    +let john = User(id: 1, firstName: "John", lastName: "Appleseed", laptop: macbook)
     
     // Save an object to a store
     try! usersStore.save(john)
    @@ -213,9 +218,6 @@ 

    3. Voilà, you’re all

    Looking to store a single item only?

    Use SingleUserDefaultsStore, it enables storing and retrieving a single value of Int, Double, String, or any Codable type.

    -

    Note about using class instead of struct

    - -

    At the moment, only final classes are supported, please take this into consideration before using the library.

    Requirements

      @@ -245,8 +247,8 @@

      License

      diff --git a/docs/js/jazzy.js b/docs/js/jazzy.js index c31dc05..1e55d6e 100755 --- a/docs/js/jazzy.js +++ b/docs/js/jazzy.js @@ -23,7 +23,7 @@ function openCurrentItemIfClosed() { if (window.jazzy.docset) { return; } - var $link = $(`.token[href="${location.hash}"]`); + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); $content = itemLinkToContent($link); if ($content.is(':hidden')) { toggleItem($link, $content); @@ -57,3 +57,14 @@ $("a:not('.token')").on('click', function() { openCurrentItemIfClosed(); } }); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/js/jquery.min.js b/docs/js/jquery.min.js index a1c07fd..b061403 100644 --- a/docs/js/jquery.min.js +++ b/docs/js/jquery.min.js @@ -1,2 +1,2 @@ -/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0=this.length)return z.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},z.QueryLexer.prototype.width=function(){return this.pos-this.start},z.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},z.QueryLexer.prototype.backup=function(){this.pos-=1},z.QueryLexer.prototype.acceptDigitRun=function(){for(var e,t;47<(t=(e=this.next()).charCodeAt(0))&&t<58;);e!=z.QueryLexer.EOS&&this.backup()},z.QueryLexer.prototype.more=function(){return this.pos0){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/js/typeahead.jquery.js b/docs/js/typeahead.jquery.js index f80bb19..3a2d2ab 100644 --- a/docs/js/typeahead.jquery.js +++ b/docs/js/typeahead.jquery.js @@ -1,15 +1,16 @@ /*! - * typeahead.js 1.2.0 - * https://github.com/twitter/typeahead.js - * Copyright 2013-2017 Twitter, Inc. and other contributors; Licensed MIT + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT */ + (function(root, factory) { if (typeof define === "function" && define.amd) { define([ "jquery" ], function(a0) { return factory(a0); }); - } else if (typeof exports === "object") { + } else if (typeof module === "object" && module.exports) { module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); @@ -482,6 +483,7 @@ 40: "down" }; function Input(o, www) { + var id; o = o || {}; if (!o.input) { $.error("input is missing"); @@ -489,14 +491,18 @@ www.mixin(this); this.$hint = $(o.hint); this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); this.$input.attr({ - "aria-activedescendant": "", - "aria-owns": this.$input.attr("id") + "_listbox", + "aria-owns": id + "_listbox", role: "combobox", - "aria-readonly": "true", - "aria-autocomplete": "list" + "aria-autocomplete": "list", + "aria-expanded": false }); - $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); this.query = this.$input.val(); this.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); @@ -669,6 +675,9 @@ this.$input.off(".tt"); this.$overflowHelper.remove(); this.$hint = this.$input = this.$overflowHelper = $("
      "); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); } }); return Input; @@ -896,8 +905,12 @@ pending: templates.pending && _.templatify(templates.pending), header: templates.header && _.templatify(templates.header), footer: templates.footer && _.templatify(templates.footer), - suggestion: templates.suggestion || suggestionTemplate + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } function suggestionTemplate(context) { return $('
      ').attr("id", _.guid()).text(displayFn(context)); } @@ -1256,8 +1269,10 @@ var $selectable; if ($selectable = this.menu.getActiveSelectable()) { this.select($selectable) && $e.preventDefault(); - } else if ($selectable = this.menu.getTopSelectable()) { - this.autocomplete($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } } }, _onEscKeyed: function onEscKeyed() { @@ -1353,6 +1368,7 @@ }, open: function open() { if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); this.menu.open(); this._updateHint(); this.eventBus.trigger("open"); @@ -1361,6 +1377,7 @@ }, close: function close() { if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); this.menu.close(); this.input.clearHint(); this.input.resetInputValue(); @@ -1409,7 +1426,9 @@ if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { - this.input.setInputValue(data.val); + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } } else { this.input.resetInputValue(); this._updateHint(); @@ -1477,7 +1496,8 @@ }); input = new Input({ hint: $hint, - input: $input + input: $input, + menu: $menu }, www); menu = new MenuConstructor({ node: $menu, diff --git a/docs/search.json b/docs/search.json index 3679aaa..e8a1170 100644 --- a/docs/search.json +++ b/docs/search.json @@ -1 +1 @@ -{"Protocols/Identifiable.html#/s:17UserDefaultsStore12IdentifiableP2IDQa":{"name":"ID","abstract":"

      ID type.

      ","parent_name":"Identifiable"},"Protocols/Identifiable.html#/s:17UserDefaultsStore12IdentifiableP5idKeys08WritableF4PathCyx2IDQzGvpZ":{"name":"idKey","abstract":"

      Id Key.

      ","parent_name":"Identifiable"},"Protocols/Identifiable.html":{"name":"Identifiable","abstract":"

      Conform to Identifiable protocol in uniquely identified objects you want to store in a UserDefaultsStore.

      "},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16uniqueIdentifierSSvp":{"name":"uniqueIdentifier","abstract":"

      Store’s unique identifier.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7encoder10Foundation11JSONEncoderCvp":{"name":"encoder","abstract":"

      JSON encoder to be used for encoding objects to be stored.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7decoder10Foundation11JSONDecoderCvp":{"name":"decoder","abstract":"

      JSON decoder to be used to decode stored objects.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16uniqueIdentifier7encoder7decoderAByxGSgSS_10Foundation11JSONEncoderCAH11JSONDecoderCtcfc":{"name":"init(uniqueIdentifier:encoder:decoder:)","abstract":"

      Initialize store with given identifier.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyyxKF":{"name":"save(_:)","abstract":"

      Save object to store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyyxSgKF":{"name":"save(_:)","abstract":"

      Save optional object (if not nil) to store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyySayxGKF":{"name":"save(_:)","abstract":"

      Save array of m objects to store. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6object6withIdxSg2IDAA12IdentifiablePQz_tF":{"name":"object(withId:)","abstract":"

      Get object from store by its id. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7objects7withIdsSayxGSay2IDAA12IdentifiablePQzG_tF":{"name":"objects(withIds:)","abstract":"

      Get array of objects from store for array of m id values. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC10allObjectsSayxGyF":{"name":"allObjects()","abstract":"

      Get all objects from store. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6delete6withIdy2IDAA12IdentifiablePQz_tF":{"name":"delete(withId:)","abstract":"

      Delete object by its id from store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6delete7withIdsySay2IDAA12IdentifiablePQzG_tF":{"name":"delete(withIds:)","abstract":"

      Delete objects with ids from given m ids array. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC9deleteAllyyF":{"name":"deleteAll()","abstract":"

      Delete all objects in store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC12objectsCountSivp":{"name":"objectsCount","abstract":"

      Count of all objects in store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC9hasObject6withIdSb2IDAA12IdentifiablePQz_tF":{"name":"hasObject(withId:)","abstract":"

      Check if store has object with given id. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7forEachyyyxXEF":{"name":"forEach(_:)","abstract":"

      Iterate over all objects in store. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16uniqueIdentifierSSvp":{"name":"uniqueIdentifier","abstract":"

      Store’s unique identifier.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C7encoder10Foundation11JSONEncoderCvp":{"name":"encoder","abstract":"

      JSON encoder to be used for encoding object to be stored.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C7decoder10Foundation11JSONDecoderCvp":{"name":"decoder","abstract":"

      JSON decoder to be used to decode the stored object.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16uniqueIdentifier7encoder7decoderACyxGSgSS_10Foundation11JSONEncoderCAI11JSONDecoderCtcfc":{"name":"init(uniqueIdentifier:encoder:decoder:)","abstract":"

      Initialize store with given identifier.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C4saveyyxKF":{"name":"save(_:)","abstract":"

      Save object to store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C6objectxSgvp":{"name":"object","abstract":"

      Get object from store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C6deleteyyF":{"name":"delete()","abstract":"

      Delete object from store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html":{"name":"SingleUserDefaultsStore","abstract":"

      SingleUserDefaultsStore offers a convenient way to store a single Codable object in UserDefaults.

      "},"Classes/UserDefaultsStore.html":{"name":"UserDefaultsStore","abstract":"

      UserDefaultsStore offers a convenient way to store a collection of Codable objects in UserDefaults.

      "},"Classes.html":{"name":"Classes","abstract":"

      The following classes are available globally.

      "},"Protocols.html":{"name":"Protocols","abstract":"

      The following protocols are available globally.

      "}} \ No newline at end of file +{"Classes/UserDefaultsStore/Snapshot.html#/s:17UserDefaultsStoreAAC8SnapshotV7objectsSayxGvp":{"name":"objects","abstract":"

      Array of objects.

      ","parent_name":"Snapshot"},"Classes/UserDefaultsStore/Snapshot.html#/s:17UserDefaultsStoreAAC8SnapshotV11dateCreated10Foundation4DateVvp":{"name":"dateCreated","abstract":"

      Date when the snapshot was created.

      ","parent_name":"Snapshot"},"Classes/UserDefaultsStore/Snapshot.html#/s:17UserDefaultsStoreAAC8SnapshotV7objects11dateCreatedADyx_GSayxG_10Foundation4DateVtcfc":{"name":"init(objects:dateCreated:)","abstract":"

      Create a new Snapshot.

      ","parent_name":"Snapshot"},"Classes/UserDefaultsStore/Snapshot.html#/s:17UserDefaultsStoreAAC8SnapshotVAASQRzrlE2eeoiySbADyx_G_AFtFZ":{"name":"==(_:_:)","abstract":"

      Returns a Boolean value indicating whether two snapshots are equal.

      ","parent_name":"Snapshot"},"Classes/UserDefaultsStore/Snapshot.html":{"name":"Snapshot","abstract":"

      Used to backup and restore content to store.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16uniqueIdentifierSSvp":{"name":"uniqueIdentifier","abstract":"

      Store’s unique identifier.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7encoder10Foundation11JSONEncoderCvp":{"name":"encoder","abstract":"

      JSON encoder to be used for encoding objects to be stored.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7decoder10Foundation11JSONDecoderCvp":{"name":"decoder","abstract":"

      JSON decoder to be used to decode stored objects.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16uniqueIdentifier7encoder7decoderAByxGSS_10Foundation11JSONEncoderCAG11JSONDecoderCtcfc":{"name":"init(uniqueIdentifier:encoder:decoder:)","abstract":"

      Initialize store with given identifier. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyyxKF":{"name":"save(_:)","abstract":"

      Save object to store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyyxSgKF":{"name":"save(_:)","abstract":"

      Save optional object (if not nil) to store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC4saveyySayxGKF":{"name":"save(_:)","abstract":"

      Save array of m objects to store. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6object6withIdxSg2IDs12IdentifiablePQz_tF":{"name":"object(withId:)","abstract":"

      Get object from store by its id. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7objects7withIdsSayxGSay2IDs12IdentifiablePQzG_tF":{"name":"objects(withIds:)","abstract":"

      Get array of objects from store for array of m id values. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC10allObjectsSayxGyF":{"name":"allObjects()","abstract":"

      Get all objects from store. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6delete6withIdy2IDs12IdentifiablePQz_tF":{"name":"delete(withId:)","abstract":"

      Delete object by its id from store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC6delete7withIdsySay2IDs12IdentifiablePQzG_tF":{"name":"delete(withIds:)","abstract":"

      Delete objects with ids from given m ids array. O(m)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC9deleteAllyyF":{"name":"deleteAll()","abstract":"

      Delete all objects in store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC12objectsCountSivp":{"name":"objectsCount","abstract":"

      Count of all objects in store. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC9hasObject6withIdSb2IDs12IdentifiablePQz_tF":{"name":"hasObject(withId:)","abstract":"

      Check if store has object with given id. O(1)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC7forEachyyyxXEF":{"name":"forEach(_:)","abstract":"

      Iterate over all objects in store. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16generateSnapshotAB0E0Vyx_GyF":{"name":"generateSnapshot()","abstract":"

      Generate a snapshot that can be saved and restored later. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC15restoreSnapshotyyAB0E0Vyx_GKF":{"name":"restoreSnapshot(_:)","abstract":"

      Restore a pre-generated Snapshot. O(n)

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC16lastSnapshotDate10Foundation0F0VSgvp":{"name":"lastSnapshotDate","abstract":"

      Date when the last Snapshot was generated.

      ","parent_name":"UserDefaultsStore"},"Classes/UserDefaultsStore.html#/s:17UserDefaultsStoreAAC15lastRestoreDate10Foundation0F0VSgvp":{"name":"lastRestoreDate","abstract":"

      Date when the last Snapshot was successfully restored.

      ","parent_name":"UserDefaultsStore"},"Classes/SingleUserDefaultsStore/Snapshot.html#/s:17UserDefaultsStore06SingleabC0C8SnapshotV6objectxSgvp":{"name":"object","abstract":"

      Object.

      ","parent_name":"Snapshot"},"Classes/SingleUserDefaultsStore/Snapshot.html#/s:17UserDefaultsStore06SingleabC0C8SnapshotV11dateCreated10Foundation4DateVvp":{"name":"dateCreated","abstract":"

      Date when the snapshot was created.

      ","parent_name":"Snapshot"},"Classes/SingleUserDefaultsStore/Snapshot.html#/s:17UserDefaultsStore06SingleabC0C8SnapshotV6object11dateCreatedAEyx_GxSg_10Foundation4DateVtcfc":{"name":"init(object:dateCreated:)","abstract":"

      Create a new Snapshot.

      ","parent_name":"Snapshot"},"Classes/SingleUserDefaultsStore/Snapshot.html#/s:17UserDefaultsStore06SingleabC0C8SnapshotVAASQRzrlE2eeoiySbAEyx_G_AGtFZ":{"name":"==(_:_:)","abstract":"

      Returns a Boolean value indicating whether two snapshots are equal.

      ","parent_name":"Snapshot"},"Classes/SingleUserDefaultsStore/Snapshot.html":{"name":"Snapshot","abstract":"

      Used to backup and restore content to store.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16uniqueIdentifierSSvp":{"name":"uniqueIdentifier","abstract":"

      Store’s unique identifier.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C7encoder10Foundation11JSONEncoderCvp":{"name":"encoder","abstract":"

      JSON encoder to be used for encoding object to be stored.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C7decoder10Foundation11JSONDecoderCvp":{"name":"decoder","abstract":"

      JSON decoder to be used to decode the stored object.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16uniqueIdentifier7encoder7decoderACyxGSS_10Foundation11JSONEncoderCAH11JSONDecoderCtcfc":{"name":"init(uniqueIdentifier:encoder:decoder:)","abstract":"

      Initialize store with given identifier. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C4saveyyxKF":{"name":"save(_:)","abstract":"

      Save object to store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C6objectxSgvp":{"name":"object","abstract":"

      Get object from store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C6deleteyyF":{"name":"delete()","abstract":"

      Delete object from store. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16generateSnapshotAC0F0Vyx_GyF":{"name":"generateSnapshot()","abstract":"

      Generate a snapshot that can be saved and restored later. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C15restoreSnapshotyyAC0F0Vyx_GKF":{"name":"restoreSnapshot(_:)","abstract":"

      Restore a pre-generated Snapshot. O(1)

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C16lastSnapshotDate10Foundation0G0VSgvp":{"name":"lastSnapshotDate","abstract":"

      Date when the last Snapshot was generated.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html#/s:17UserDefaultsStore06SingleabC0C15lastRestoreDate10Foundation0G0VSgvp":{"name":"lastRestoreDate","abstract":"

      Date when the last Snapshot was successfully restored.

      ","parent_name":"SingleUserDefaultsStore"},"Classes/SingleUserDefaultsStore.html":{"name":"SingleUserDefaultsStore","abstract":"

      SingleUserDefaultsStore offers a convenient way to store a single Codable object in UserDefaults.

      "},"Classes/UserDefaultsStore.html":{"name":"UserDefaultsStore","abstract":"

      UserDefaultsStore offers a convenient way to store a collection of Codable objects in UserDefaults.

      "},"Classes.html":{"name":"Classes","abstract":"

      The following classes are available globally.

      "}} \ No newline at end of file