├── .gitignore ├── Sources ├── ToKana │ └── main.swift ├── ToRomaji │ └── main.swift ├── CLIUtils │ └── cliutils.swift └── WanaKana │ ├── ConversionFunctions.swift │ ├── Options.swift │ ├── ClassificationFunctions.swift │ ├── WanaKana.swift │ └── Resources │ └── wanakana.js ├── Tests ├── LinuxMain.swift └── WanaKanaTests │ ├── XCTestManifests.swift │ ├── WanaKanaMiscTests.swift │ ├── WanaKanaClassificationTests.swift │ └── WanaKanaConversionTests.swift ├── Package.swift ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | *~ 7 | -------------------------------------------------------------------------------- /Sources/ToKana/main.swift: -------------------------------------------------------------------------------- 1 | import WanaKana 2 | import CLIUtils 3 | 4 | convert(WanaKana.toKana) 5 | -------------------------------------------------------------------------------- /Sources/ToRomaji/main.swift: -------------------------------------------------------------------------------- 1 | import WanaKana 2 | import CLIUtils 3 | 4 | convert(WanaKana.toRomaji) 5 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import WanaKanaTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += WanaKanaTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/WanaKanaTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(WanaKanaClassificationTests.allTests), 7 | testCase(WanaKanaConversionTests.allTests), 8 | testCase(WanaKanaMiscTests.allTests), 9 | ] 10 | } 11 | #endif 12 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "WanaKana", 7 | products: [ 8 | .library( 9 | name: "WanaKana", 10 | targets: ["WanaKana"]), 11 | .library( 12 | name: "CLIUtils", 13 | targets: ["CLIUtils"]), 14 | .executable( 15 | name: "tokana", 16 | targets: ["ToKana"]), 17 | .executable( 18 | name: "toromaji", 19 | targets: ["ToRomaji"]), 20 | ], 21 | dependencies: [ 22 | ], 23 | targets: [ 24 | .target( 25 | name: "ToKana", 26 | dependencies: ["WanaKana", "CLIUtils"]), 27 | .target( 28 | name: "ToRomaji", 29 | dependencies: ["WanaKana", "CLIUtils"]), 30 | .target( 31 | name: "CLIUtils", 32 | dependencies: []), 33 | .target( 34 | name: "WanaKana", 35 | dependencies: [], 36 | resources: [ 37 | .copy("Resources/wanakana.js"), 38 | ]), 39 | .testTarget( 40 | name: "WanaKanaTests", 41 | dependencies: ["WanaKana"]), 42 | ] 43 | ) 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The file wanakana.js was written by Mims H. Wright and Duncan Bay and is licensed under the terms of the MIT license. 2 | 3 | All Swift files licensed as follows: 4 | 5 | Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /Sources/CLIUtils/cliutils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // cliutils.swift 3 | // WanaKanaSwift 4 | // 5 | // Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | // Redistribution and use in source and binary forms, with or without modification, 7 | // are permitted provided that the following conditions are met: 8 | // 1. Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holder nor the names of its contributors may be 14 | // used to endorse or promote products derived from this software without specific prior 15 | // written permission. 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | import Foundation 27 | import WanaKana 28 | import func Darwin.fputs 29 | import var Darwin.stderr 30 | import var Darwin.stdout 31 | 32 | public typealias Converter = (String, Options?) -> String? 33 | 34 | public func convert(_ converter: Converter) { 35 | let text = CommandLine.arguments.dropFirst().joined(separator: " ") 36 | 37 | if !text.isEmpty { 38 | guard let response = converter(text, nil) else { 39 | fputs("some sort of error message\n", stderr) 40 | exit(1) 41 | } 42 | 43 | fputs(response + "\n", stdout) 44 | } else { 45 | guard let text = readLine() else { 46 | fputs("some sort of error message\n", stderr) 47 | exit(1) 48 | } 49 | 50 | guard let response = converter(text, nil) else { 51 | fputs("some sort of error message\n", stderr) 52 | exit(1) 53 | } 54 | 55 | fputs(response + "\n", stdout) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/WanaKana/ConversionFunctions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConversionFunctions.swift 3 | // WanaKanaSwift 4 | // 5 | // Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | // Redistribution and use in source and binary forms, with or without modification, 7 | // are permitted provided that the following conditions are met: 8 | // 1. Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holder nor the names of its contributors may be 14 | // used to endorse or promote products derived from this software without specific prior 15 | // written permission. 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | extension WanaKana { 27 | private static func toSpecifiedScript(input: String, script: Script, options: Options? = nil) -> String? { 28 | let options = options ?? Options() 29 | let js = "wanakana.to\(script.rawValue)(\"\(input)\", \(options))" 30 | 31 | guard let result = jsc.evaluateScript(js), !result.isUndefined else { 32 | return nil 33 | } 34 | 35 | return result.toString() 36 | } 37 | 38 | public static func toHiragana(_ input: String, options: Options? = nil) -> String? { 39 | return toSpecifiedScript(input: input, script: .hiragana, options: options) 40 | } 41 | 42 | public static func toKana(_ input: String, options: Options? = nil) -> String? { 43 | return toSpecifiedScript(input: input, script: .kana, options: options) 44 | } 45 | 46 | public static func toKatakana(_ input: String, options: Options? = nil) -> String? { 47 | return toSpecifiedScript(input: input, script: .katakana, options: options) 48 | } 49 | 50 | public static func toRomaji(_ input: String, options: Options? = nil) -> String? { 51 | return toSpecifiedScript(input: input, script: .romaji, options: options) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/WanaKana/Options.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Options.swift 3 | // WanaKanaSwift 4 | // 5 | // Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | // Redistribution and use in source and binary forms, with or without modification, 7 | // are permitted provided that the following conditions are met: 8 | // 1. Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holder nor the names of its contributors may be 14 | // used to endorse or promote products derived from this software without specific prior 15 | // written permission. 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | public struct Options: CustomStringConvertible { 27 | public enum Romanization: String { 28 | case hepburn 29 | } 30 | 31 | public enum IMEMode: String { 32 | case on = "true" 33 | case off = "false" 34 | case toHiragana 35 | case toKatakana 36 | } 37 | 38 | let useObsoleteKana: Bool 39 | let passRomaji: Bool 40 | let upcaseKatakana: Bool 41 | let imeMode: IMEMode 42 | let romanization: Romanization 43 | 44 | public init(useObsoleteKana: Bool = false, 45 | passRomaji: Bool = false, 46 | upcaseKatakana: Bool = false, 47 | imeMode: IMEMode = .off, 48 | romanization: Romanization = .hepburn 49 | ) { 50 | self.useObsoleteKana = useObsoleteKana 51 | self.passRomaji = passRomaji 52 | self.upcaseKatakana = upcaseKatakana 53 | self.imeMode = imeMode 54 | self.romanization = romanization 55 | } 56 | 57 | public var description: String { 58 | var result = "{" 59 | 60 | result += "useObsoleteKana: \(useObsoleteKana)" 61 | result += ", passRomaji: \(passRomaji)" 62 | result += ", upcaseKatakana: \(upcaseKatakana)" 63 | result += ", IMEMode: \(imeMode.rawValue) }" 64 | // Since romanization only supports one value, we won't bother including it. 65 | 66 | return result 67 | } 68 | } 69 | 70 | public enum TokenType: String { 71 | case englishNumeral 72 | case en 73 | case space 74 | case englishPunctuation 75 | case kanji 76 | case hiragana 77 | case katakana 78 | case japaneseNumeral 79 | case japanesePunctuation 80 | case ja 81 | case other 82 | } 83 | 84 | public struct TokenDetail: Equatable { 85 | let type: TokenType 86 | let value: String 87 | } 88 | -------------------------------------------------------------------------------- /Sources/WanaKana/ClassificationFunctions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassificationFunctions.swift 3 | // WanaKanaSwift 4 | // 5 | // Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | // Redistribution and use in source and binary forms, with or without modification, 7 | // are permitted provided that the following conditions are met: 8 | // 1. Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holder nor the names of its contributors may be 14 | // used to endorse or promote products derived from this software without specific prior 15 | // written permission. 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | extension WanaKana { 27 | private static func isSpecifiedScript(input: String, script: Script, 28 | option: String? = nil) -> Bool { 29 | let js: String 30 | if let option = option { 31 | js = "wanakana.is\(script.rawValue)('\(input)', \(option))" 32 | } else { 33 | js = "wanakana.is\(script.rawValue)('\(input)')" 34 | } 35 | guard let result = jsc.evaluateScript(js), !result.isUndefined else { 36 | return false 37 | } 38 | 39 | return result.toBool() 40 | } 41 | 42 | public static func isHiragana(_ input: String) -> Bool { 43 | return isSpecifiedScript(input: input, script: .hiragana) 44 | } 45 | 46 | public static func isJapanese(_ input: String, allowed: String? = nil) -> Bool { 47 | return isSpecifiedScript(input: input, script: .japanese, option: allowed) 48 | } 49 | 50 | public static func isKana(_ input: String) -> Bool { 51 | return isSpecifiedScript(input: input, script: .kana) 52 | } 53 | 54 | public static func isKanji(_ input: String) -> Bool { 55 | return isSpecifiedScript(input: input, script: .kanji) 56 | } 57 | 58 | public static func isKatakana(_ input: String) -> Bool { 59 | return isSpecifiedScript(input: input, script: .katakana) 60 | } 61 | 62 | public static func isMixed(_ input: String, passKanji: Bool = true) -> Bool { 63 | let option = (passKanji) ? nil : "{ passKanji : false }" 64 | return isSpecifiedScript(input: input, script: .mixed, option: option) 65 | } 66 | 67 | public static func isRomaji(_ input: String, allowed: String? = nil) -> Bool { 68 | return isSpecifiedScript(input: input, script: .romaji, option: allowed) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/WanaKana/WanaKana.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WanaKana.swift 3 | // WanaKanaSwift 4 | // 5 | // Copyright (c) 2019-2021 BlueDino Software (http://bluedino.net) 6 | // Redistribution and use in source and binary forms, with or without modification, 7 | // are permitted provided that the following conditions are met: 8 | // 1. Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holder nor the names of its contributors may be 14 | // used to endorse or promote products derived from this software without specific prior 15 | // written permission. 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | import Foundation 27 | import JavaScriptCore 28 | 29 | public struct WanaKana { 30 | enum Script: String { 31 | case hiragana = "Hiragana" 32 | case japanese = "Japanese" 33 | case kana = "Kana" 34 | case kanji = "Kanji" 35 | case katakana = "Katakana" 36 | case mixed = "Mixed" 37 | case romaji = "Romaji" 38 | } 39 | 40 | static let jsc: JSContext! = { 41 | let jsc = JSContext() 42 | 43 | guard let scriptURL = Bundle.module.url(forResource: "wanakana", withExtension: "js") else { 44 | print("Error getting URL of script") 45 | return nil 46 | } 47 | 48 | guard let script = try? String(contentsOf: scriptURL, encoding: .utf8) else { 49 | print("Error loading wanakana.js") 50 | return nil 51 | } 52 | 53 | guard let result = jsc?.evaluateScript(script), result.toBool() == true else { 54 | print("Evaluating wanakana.js did not return true.") 55 | return nil 56 | } 57 | 58 | return jsc 59 | }() 60 | 61 | // MARK: - Miscellaneous Methods 62 | 63 | public static func stripOkurigana(_ input: String, leading: Bool = false, match: String? = nil) -> String { 64 | let matchOption: String 65 | if let match = match { 66 | matchOption = "'\(match)'" 67 | } else { 68 | matchOption = "''" 69 | } 70 | 71 | let js = "wanakana.stripOkurigana('\(input)', { leading : \(leading), matchKanji: \(matchOption) })" 72 | guard let result = jsc.evaluateScript(js), !result.isUndefined else { 73 | return "" 74 | } 75 | 76 | return result.toString() 77 | } 78 | 79 | public static func tokenize(_ input: String, compact: Bool = false) -> [String] { 80 | let js = "wanakana.tokenize('\(input)', {compact: \(compact)})" 81 | guard let result = jsc.evaluateScript(js), !result.isUndefined else { 82 | return [] 83 | } 84 | 85 | var a: [String] = [] 86 | for e in result.toArray() { 87 | if let s = e as? String { 88 | a.append(s) 89 | } 90 | } 91 | 92 | return a 93 | } 94 | 95 | public static func tokenizeDetailed(_ input: String, compact: Bool = false) -> [TokenDetail] { 96 | let js = "wanakana.tokenize('\(input)', {compact: \(compact), detailed: true})" 97 | guard let result = jsc.evaluateScript(js), !result.isUndefined else { 98 | return [] 99 | } 100 | 101 | var a: [TokenDetail] = [] 102 | for e in result.toArray() { 103 | if let e = e as? Dictionary, 104 | let rawType = e["type"], 105 | let type = TokenType(rawValue: rawType), 106 | let value = e["value"] { 107 | let s = TokenDetail(type: type, value: value) 108 | a.append(s) 109 | } 110 | } 111 | 112 | return a 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Tests/WanaKanaTests/WanaKanaMiscTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import WanaKana 3 | 4 | final class WanaKanaMiscTests: XCTestCase { 5 | func testStripOkurigana() { 6 | XCTAssertEqual(WanaKana.stripOkurigana(""), "", "sanity check--empty string") 7 | XCTAssertEqual(WanaKana.stripOkurigana("ふふフフ"), "ふふフフ", "basic test ふふフフ") 8 | XCTAssertEqual(WanaKana.stripOkurigana("abc"), "abc", "basic test abc") 9 | XCTAssertEqual(WanaKana.stripOkurigana("ふaふbフcフ"), "ふaふbフcフ", "basic test ふaふbフcフ") 10 | XCTAssertEqual(WanaKana.stripOkurigana("踏み込む"), "踏み込", "basic test 踏み込む") 11 | XCTAssertEqual(WanaKana.stripOkurigana("使い方"), "使い方", "basic test 使い方") 12 | XCTAssertEqual(WanaKana.stripOkurigana("申し申し"), "申し申", "basic test 申し申し") 13 | XCTAssertEqual(WanaKana.stripOkurigana("お腹"), "お腹", "basic test お腹") 14 | XCTAssertEqual(WanaKana.stripOkurigana("お祝い"), "お祝", "basic test お祝い") 15 | 16 | XCTAssertEqual(WanaKana.stripOkurigana("踏み込む", leading: true), "踏み込む", "leading test 踏み込む") 17 | XCTAssertEqual(WanaKana.stripOkurigana("お腹", leading: true), "腹", "leading test お腹") 18 | XCTAssertEqual(WanaKana.stripOkurigana("お祝い", leading: true), "祝い", "leading test お祝い") 19 | 20 | XCTAssertEqual(WanaKana.stripOkurigana("おはら", match: "お腹"), "おはら", "match kanji おはら") 21 | XCTAssertEqual(WanaKana.stripOkurigana("ふみこむ", match: "踏み込む"), "ふみこ", "match kanji ふみこむ") 22 | 23 | XCTAssertEqual(WanaKana.stripOkurigana("おみまい", leading: true, match: "お祝い"), "みまい", "leading match kanji おみまい") 24 | XCTAssertEqual(WanaKana.stripOkurigana("おはら", leading: true, match: "お腹"), "はら", "leading match kanji おはら") 25 | } 26 | 27 | func testTokenize() { 28 | XCTAssertEqual(WanaKana.tokenize(""), [], "sanity check--empty string") 29 | XCTAssertEqual(WanaKana.tokenize("ふふ"), ["ふふ"], "basic test ふふ") 30 | XCTAssertEqual(WanaKana.tokenize("フフ"), ["フフ"], "basic test フフ") 31 | XCTAssertEqual(WanaKana.tokenize("ふふフフ"), ["ふふ", "フフ"], "basic test ふふフフ") 32 | XCTAssertEqual(WanaKana.tokenize("阮咸"), ["阮咸"], "basic test 阮咸") 33 | XCTAssertEqual(WanaKana.tokenize("感じ"), ["感", "じ"], "basic test 感じ") 34 | XCTAssertEqual(WanaKana.tokenize("私は悲しい"), ["私", "は", "悲", "しい"], "basic test 私は悲しい") 35 | XCTAssertEqual(WanaKana.tokenize("ok لنذهب!"), ["ok"," ","لنذهب", "!"], "basic test ok لنذهب!") 36 | XCTAssertEqual(WanaKana.tokenize("5romaji here...!?漢字ひらがなカタ カナ4「SHIO」。!"), 37 | ["5", "romaji", " ", "here", "...!?", 38 | "漢字", "ひらがな", "カタ", " ", 39 | "カナ", "4", "「", "SHIO", "」。!"], "handles mixed input") 40 | XCTAssertEqual(WanaKana.tokenize("5romaji here...!?漢字ひらがなカタ カナ4「SHIO」。!", compact: false), 41 | ["5", "romaji", " ", "here", "...!?", 42 | "漢字", "ひらがな", "カタ", " ", 43 | "カナ", "4", "「", "SHIO", "」。!"], "compact option explicitly false") 44 | XCTAssertEqual(WanaKana.tokenize("5romaji here...!?漢字ひらがなカタ カナ4「SHIO」。! لنذهب", compact: true), 45 | ["5", "romaji here", "...!?", "漢字ひらがなカタ カナ", 46 | "4「", "SHIO", "」。!", " ", "لنذهب"], "compact option true") 47 | 48 | 49 | XCTAssertEqual(WanaKana.tokenizeDetailed("5romaji here...!?漢字ひらがなカタ カナ4「SHIO」。! لنذهب"), 50 | [TokenDetail(type: .englishNumeral, value: "5"), 51 | TokenDetail(type: .en, value: "romaji"), 52 | TokenDetail(type: .space, value: " "), 53 | TokenDetail(type: .en, value: "here"), 54 | TokenDetail(type: .englishPunctuation, value: "...!?"), 55 | TokenDetail(type: .kanji, value: "漢字"), 56 | TokenDetail(type: .hiragana, value: "ひらがな"), 57 | TokenDetail(type: .katakana, value: "カタ"), 58 | TokenDetail(type: .space, value: " "), 59 | TokenDetail(type: .katakana, value: "カナ"), 60 | TokenDetail(type: .japaneseNumeral, value: "4"), 61 | TokenDetail(type: .japanesePunctuation, value: "「"), 62 | TokenDetail(type: .ja, value: "SHIO"), 63 | TokenDetail(type: .japanesePunctuation, value: "」。!"), 64 | TokenDetail(type: .space, value: " "), 65 | TokenDetail(type: .other, value: "لنذهب"), 66 | ], "detailed") 67 | XCTAssertEqual(WanaKana.tokenizeDetailed("5romaji here...!?漢字ひらがなカタ カナ4「SHIO」。! لنذهب", compact: true), 68 | [TokenDetail(type: .other, value: "5"), 69 | TokenDetail(type: .en, value: "romaji here"), 70 | TokenDetail(type: .other, value: "...!?"), 71 | TokenDetail(type: .ja, value: "漢字ひらがなカタ カナ"), 72 | TokenDetail(type: .other, value: "4「"), 73 | TokenDetail(type: .ja, value: "SHIO"), 74 | TokenDetail(type: .other, value: "」。!"), 75 | TokenDetail(type: .en, value: " "), 76 | TokenDetail(type: .other, value: "لنذهب"), 77 | ], "compact detailed") 78 | } 79 | 80 | static var allTests = [ 81 | ("testStripOkurigana", testStripOkurigana), 82 | ("testTokenize", testTokenize), 83 | ] 84 | } 85 | 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WanaKana 2 | 3 | [WanaKana.js](https://wanakana.com) is a mobile-friendly and lightweight Japanese input method editor (IME). Currently (early 2021), there are ports to [Java](https://github.com/MasterKale/WanaKanaJava), [Python](https://github.com/Starwort/wanakana-py) and [Rust](https://github.com/PSeitz/wana_kana_rust). 4 | 5 | **WanaKanaSwift** is a port to Swift; I suppose technically it's more a wrapper than a port. I first wrote this code in 2019 for use in a couple of iOS apps. I decided to clean it up an make it available. 6 | 7 | An actual rewrite of the library in Swift is in process—hence the inclusion of most of the tests from the original JavaScript version. At some point soon, I'll include it as a branch in this repo. 8 | 9 | ### Installation 10 | 11 | WanaKanaSwift is made available as a Swift package so there is no installation, *per se* needed. Rather you need to include the package as a dependency in your own Swift project. If you are using Xcode, then select the menu option `New > Swift Packages > Add Package Dependency...`. If you are not using Xcode, you can specify the dependency in your `Package.swift` with the following: 12 | 13 | .package(url: "https://github.com/profburke/wanakanaswift", from: "1.0.0") 14 | 15 | NOTE: The current version of WanaKanaSwift is 0.5.0. The project uses version numbers that follow the conventions outlined at [semver.org](https://semver.org). Check the tags in the Github repo to verify the current version and update the above dependency specifier as appropriate. 16 | 17 | ##### `tokana` and `toromaji` executables 18 | 19 | In addition to a library for use in your Swift project, this package also includes two, simple CLI utilities `tokana` and `toromaji`. 20 | 21 | The easiest way to build and install these two utilities is from the command line. `cd` to the package's directory and run `swift build`. 22 | The two executables will be in `.build/debug`. Copy these into a directory on your `PATH`, such as `/usr/local/bin` and you're ready to roll. 23 | 24 | ### Usage 25 | 26 | After importing the package, you can use the functinality by calling the appropriate function. All functions are declared as static functions on 27 | the `WanaKana` struct (*basically the struct 28 | is just there for name-spacing*). Examples follow: 29 | 30 | ```swift 31 | import WanaKana 32 | 33 | print(WanaKana.isJapanese("泣き虫")) # prints true 34 | 35 | print(WanaKana.isHiragana("A")) # prints false 36 | 37 | let result = WanaKana.toHiragana("スーパー") # result = "すうぱあ" 38 | 39 | let result = WanaKana.stripOkurigana("お腹", leading: true), # result = "腹" 40 | ``` 41 | 42 | For more details, see the extended API documentation for the original JavaScript version: [https://wanakana.com/docs/global.html](https://wanakana.com/docs/global.html). 43 | 44 | ##### `tokana` and `toromaji` executables 45 | 46 | Both of these CLI tools support reciving input either through piping 47 | 48 | echo "haiku toukyou oosaka" | tokana 49 | 50 | or as command line arguments 51 | 52 | toromaji とうきょう おおさか 53 | 54 | ### How to Contribute 55 | 56 | Thank you for taking the time to contribute! 57 | 58 | There are many ways to contribute in addition to submitting code. Bug reports, feature suggestions, a logo for the project, and improvements to documentation are all appreciated. 59 | 60 | ##### Bug Reports 61 | 62 | Please submit bug reports and feature suggestions by creating a [new issue](https://github.com/profburke/wanakanaswift/issues/new). If possible, look for an existing [open issue](https://github.com/profburke/wanakanaswift/issues) that is related and comment on it. 63 | 64 | When creating an issue, the more detail, the better. For bug reports in partciular, try to include at least the following information: 65 | 66 | - The library version 67 | - The operating system (macOS, Windows, etc) and version 68 | - The expected behavior 69 | - The observed behavior 70 | - Step-by-step instructions for reproducing the bug 71 | 72 | ##### Pull Requests 73 | 74 | Ensure the PR description clearly describes the problem and solution. It should include the relevant issue number, if applicable. 75 | 76 | ##### Documentation Improvements 77 | 78 | Preferably, submit documentation changes by pull request. However, feel free to post your changes to an issue or send them to the project team. 79 | 80 | #### TODO 81 | 82 | - Documentation is sorely lacking... 83 | - The `Options` struct is missing the following fields from the JavaScript version: 84 | - custom kana mapping 85 | - custom romaji mapping 86 | - The following need improved error handling: 87 | - initing JSC 88 | - `isSpecifiedScript()` 89 | - `toSpecifiedScript()` 90 | - `stripOkurigana()` 91 | - `tokenize()` 92 | - evaluate script failure 93 | - casting failure 94 | - `tokenizeDetailed()` 95 | - evaluate script failure 96 | - casting, etc failure in loop 97 | - Replace stringly-typing of allowed parameter in `isJapanese()` and `isRomaji()` with something type safe 98 | - The following tests from the JavaScript version are missing: 99 | - `testToKana()`: Will convert punctuation but pass through spaces 100 | - `testToKana()`: splitIntoConvertedKana tests 101 | - `testToRomaji()`: Will convert punctuation and full-width spaces 102 | - Research means for combining `tokenize()` and `tokenizeDetailed()`. 103 | 104 | 105 | ##### Notes: 106 | 107 | For all error handling consider making use of the `Result` type. 108 | 109 | For combining `tokenize()` and `tokenizeDetailed()`: Perhaps create a type that behaves like an array of strings but can be queried for additional details. 110 | 111 | 112 | ### LICENSE 113 | 114 | The JavaScript file is licensed under the MIT license and was written by Mims H. Wright and Duncan Bay. 115 | 116 | All Swift code written by Matthew M. Burke and licensed according to the terms in the [LICENSE](https://github.com/profburke/WanaKanaSwift/blob/main/LICENSE) file (also included in each Swift file). 117 | 118 | -------------------------------------------------------------------------------- /Tests/WanaKanaTests/WanaKanaClassificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import WanaKana 3 | 4 | final class WanaKanaClassificationTests: XCTestCase { 5 | func testIsHiragana() { 6 | XCTAssertFalse(WanaKana.isHiragana(""), "sane defaults-empty string") 7 | XCTAssertTrue(WanaKana.isHiragana("あ"), "あ is hiragana") 8 | XCTAssertTrue(WanaKana.isHiragana("ああ"), "ああ is hiragana") 9 | XCTAssertFalse(WanaKana.isHiragana("ア"), "ア is not hiragana") 10 | XCTAssertFalse(WanaKana.isHiragana("A"), "A is not hiragana") 11 | XCTAssertFalse(WanaKana.isHiragana("あア"), "あア is not hiragana") 12 | XCTAssertTrue(WanaKana.isHiragana("げーむ"), "ignores long dashes in hiragana") 13 | } 14 | 15 | func testIsJapanese() { 16 | XCTAssertFalse(WanaKana.isJapanese(""), "sane defaults-empty string") 17 | XCTAssertTrue(WanaKana.isJapanese("泣き虫"), "泣き虫 is Japanese") 18 | XCTAssertTrue(WanaKana.isJapanese("あア"), "あア is Japanese") 19 | XCTAssertFalse(WanaKana.isJapanese("A泣き虫"), "A泣き虫 is not Japanese") 20 | XCTAssertFalse(WanaKana.isJapanese("A"), "A is not Japanese") 21 | XCTAssertTrue(WanaKana.isJapanese(" "), "ja space is Japanese") 22 | XCTAssertFalse(WanaKana.isJapanese(" "), "en space is not Japanese") 23 | XCTAssertTrue(WanaKana.isJapanese("泣き虫。#!〜〈〉《》〔〕[]【】(){}〝〟"), "泣き虫。!〜 (w. zenkaku punctuation) is Japanese") 24 | XCTAssertFalse(WanaKana.isJapanese("泣き虫.!~"), "泣き虫.!~ (w. romaji punctuation) is not Japanese") 25 | XCTAssertTrue(WanaKana.isJapanese("0123456789"), "zenkaku numbers are considered neutral") 26 | XCTAssertFalse(WanaKana.isJapanese("0123456789"), "Latin numbers are not Japanese") 27 | XCTAssertTrue(WanaKana.isJapanese("MeToo"), "zenkaku latin letters are considered neutral") 28 | XCTAssertTrue(WanaKana.isJapanese("2011年"), "mixed with numbers is Japanese") 29 | XCTAssertTrue(WanaKana.isJapanese("ハンカクカタカナ"), "hankaku katakana is allowed") 30 | XCTAssertTrue(WanaKana.isJapanese("#MeToo、これを前に「KUROSHIO」は、都内で報道陣を前に水中探査ロボットの最終点検の様子を公開しました。イルカのような形をした探査ロボットは、全長3メートル、重さは350キロあります。《はじめに》冒頭、安倍総理大臣は、ことしが明治元年から150年にあたることに触れ「明治という新しい時代が育てたあまたの人材が、技術優位の欧米諸国が迫る『国難』とも呼ぶべき危機の中で、わが国が急速に近代化を遂げる原動力となった。今また、日本は少子高齢化という『国難』とも呼ぶべき危機に直面している。もう1度、あらゆる日本人にチャンスを創ることで、少子高齢化も克服できる」と呼びかけました。《働き方改革》続いて安倍総理大臣は、具体的な政策課題の最初に「働き方改革」を取り上げ、「戦後の労働基準法制定以来、70年ぶりの大改革だ。誰もが生きがいを感じて、その能力を思う存分発揮すれば少子高齢化も克服できる」と述べました。そして、同一労働同一賃金の実現や、時間外労働の上限規制の導入、それに労働時間でなく成果で評価するとして労働時間の規制から外す「高度プロフェッショナル制度」の創設などに取り組む考えを強調しました。"), "randomly sliced nhk news text is Japanese") 31 | XCTAssertTrue(WanaKana.isJapanese("≪偽括弧≫", allowed: "/[≪≫]/"), "accepts optional allowed chars") 32 | } 33 | 34 | func testIsKana() { 35 | XCTAssertFalse(WanaKana.isKana(""), "sane defaults-empty string") 36 | XCTAssertTrue(WanaKana.isKana("あ"), "あ is kana") 37 | XCTAssertTrue(WanaKana.isKana("ア"), "ア is kana") 38 | XCTAssertTrue(WanaKana.isKana("あア"), "あア is kana") 39 | XCTAssertFalse(WanaKana.isKana("A"), "A is not kana") 40 | XCTAssertFalse(WanaKana.isKana("あAア"), "あAア is not kana") 41 | XCTAssertTrue(WanaKana.isKana("アーあ"), "ignores long dash in mixed kana") 42 | } 43 | 44 | func testIsKanji() { 45 | XCTAssertFalse(WanaKana.isKanji(""), "sane defaults-empty string") 46 | XCTAssertTrue(WanaKana.isKanji("切腹"), "切腹 is kanji") 47 | XCTAssertTrue(WanaKana.isKanji("刀"), "刀 is kanji") 48 | XCTAssertFalse(WanaKana.isKanji("🐸"), "emoji are not kanji") 49 | XCTAssertFalse(WanaKana.isKanji("あ"), "あ is not kanji") 50 | XCTAssertFalse(WanaKana.isKanji("ア"), "ア is not kanji") 51 | XCTAssertFalse(WanaKana.isKanji("あア"), "あア is not kanji") 52 | XCTAssertFalse(WanaKana.isKanji("A"), "A is not kanji") 53 | XCTAssertFalse(WanaKana.isKanji("あAア"), "あAア is not kanji") 54 | XCTAssertFalse(WanaKana.isKanji("12隻"), "12隻 is not kanji") 55 | XCTAssertFalse(WanaKana.isKanji("12隻"), "12隻 is not kanji") 56 | XCTAssertFalse(WanaKana.isKanji("隻。"), "隻。is not kanji") 57 | } 58 | 59 | func testIsKatakana() { 60 | XCTAssertFalse(WanaKana.isKatakana(""), "sane defaults-empty string") 61 | XCTAssertTrue(WanaKana.isKatakana("アア"), "アア is katakana") 62 | XCTAssertTrue(WanaKana.isKatakana("ア"), "ア is katakana") 63 | XCTAssertFalse(WanaKana.isKatakana("あ"), "あ is not katakana") 64 | XCTAssertFalse(WanaKana.isKatakana("A"), "A is not katakana") 65 | XCTAssertFalse(WanaKana.isKatakana("あア"), "あア is not katakana") 66 | XCTAssertTrue(WanaKana.isKatakana("ゲーム"), "ignores long dash in katakana") 67 | } 68 | 69 | func testIsMixed() { 70 | XCTAssertFalse(WanaKana.isMixed(""), "sane defaults-empty string") 71 | XCTAssertTrue(WanaKana.isMixed("Aア"), "Aア is mixed") 72 | XCTAssertTrue(WanaKana.isMixed("Aあ"), "Aあ is mixed") 73 | XCTAssertTrue(WanaKana.isMixed("Aあア"), "Aあア is mixed") 74 | XCTAssertFalse(WanaKana.isMixed("2あア"), "2あア is not mixed") 75 | XCTAssertTrue(WanaKana.isMixed("お腹A"), "お腹A is mixed") 76 | XCTAssertFalse(WanaKana.isMixed("お腹A", passKanji: false), "お腹A is not mixed when passkanji: false") 77 | XCTAssertFalse(WanaKana.isMixed("お腹"), "お腹 is not mixed") 78 | XCTAssertFalse(WanaKana.isMixed("腹"), "腹 is not mixed") 79 | XCTAssertFalse(WanaKana.isMixed("A"), "A is not mixed") 80 | XCTAssertFalse(WanaKana.isMixed("あ"), "あ is not mixed") 81 | XCTAssertFalse(WanaKana.isMixed("ア"), "ア is not mixed") 82 | } 83 | 84 | func testIsRomaji() { 85 | XCTAssertFalse(WanaKana.isRomaji(""), "sane defaults-empty string") 86 | XCTAssertTrue(WanaKana.isRomaji("A"), "A is romaji") 87 | XCTAssertTrue(WanaKana.isRomaji("xYz"), "xYz is romaji") 88 | XCTAssertTrue(WanaKana.isRomaji("Tōkyō and Ōsaka"), "Tōkyō and Ōsaka is romaji") 89 | XCTAssertFalse(WanaKana.isRomaji("あアA"), "あアA is not romaji") 90 | XCTAssertFalse(WanaKana.isRomaji("お願い"), "お願い is not romaji") 91 | XCTAssertFalse(WanaKana.isRomaji("熟成"), "熟成 is not romaji") 92 | XCTAssertTrue(WanaKana.isRomaji("a*b&c-d"), "passes latin punctuation") 93 | XCTAssertTrue(WanaKana.isRomaji("0123456789"), "passes latin numbers") 94 | XCTAssertFalse(WanaKana.isRomaji("a!b&cーd"), "fails zenkaku punctuation") 95 | XCTAssertFalse(WanaKana.isRomaji("hello"), "fails zenkaku latin") 96 | XCTAssertTrue(WanaKana.isRomaji("a!b&cーd", allowed: "/[!ー]/"), "accepts optional allowed chars") 97 | } 98 | 99 | static var allTests = [ 100 | ("testIsHiragana", testIsHiragana), 101 | ("testIsJapanese", testIsJapanese), 102 | ("testIsKana", testIsKana), 103 | ("testIsKanji", testIsKanji), 104 | ("testIsKatakana", testIsKatakana), 105 | ("testIsMixed", testIsMixed), 106 | ("testIsRomaji", testIsRomaji), 107 | ] 108 | } 109 | -------------------------------------------------------------------------------- /Tests/WanaKanaTests/WanaKanaConversionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import WanaKana 3 | 4 | final class WanaKanaConversionTests: XCTestCase { 5 | func testToHiragana() { 6 | XCTAssertEqual(WanaKana.toHiragana(""), "", "sanity check--empty string") 7 | 8 | var options = Options(useObsoleteKana: true) 9 | XCTAssertEqual(WanaKana.toHiragana("IROHANIHOHETO", options: options), "いろはにほへと", 10 | "Iroha line 1") 11 | XCTAssertEqual(WanaKana.toHiragana("CHIRINURUWO", options: options), "ちりぬるを", 12 | "Iroha line 2") 13 | XCTAssertEqual(WanaKana.toHiragana("WAKAYOTARESO", options: options), "わかよたれそ", 14 | "Iroha line 3") 15 | XCTAssertEqual(WanaKana.toHiragana("TSUNENARAMU", options: options), "つねならむ", 16 | "Iroha line 4") 17 | XCTAssertEqual(WanaKana.toHiragana("UWINOOKUYAMA", options: options), "うゐのおくやま", 18 | "Iroha line 5") 19 | XCTAssertEqual(WanaKana.toHiragana("KEFUKOETE", options: options), "けふこえて", 20 | "Iroha line 6") 21 | XCTAssertEqual(WanaKana.toHiragana("ASAKIYUMEMISHI", options: options), "あさきゆめみし", 22 | "Iroha line 7") 23 | XCTAssertEqual(WanaKana.toHiragana("WEHIMOSESU", options: options), "ゑひもせす", 24 | "Iroha line 8") 25 | XCTAssertEqual(WanaKana.toHiragana("NLTU", options: options), "んっ", 26 | "not in Iroha") 27 | 28 | XCTAssertEqual(WanaKana.toHiragana("wi"), "うぃ", "use obsolete false by default") 29 | options = Options(useObsoleteKana: false) 30 | XCTAssertEqual(WanaKana.toHiragana("wi", options: options), "うぃ", 31 | "wi = うぃ (when useObsoleteKana is false)") 32 | options = Options(useObsoleteKana: true) 33 | XCTAssertEqual(WanaKana.toHiragana("wi", options: options), "ゐ", 34 | "wi = ゐ (when useObsoleteKana is true)") 35 | XCTAssertEqual(WanaKana.toHiragana("we", options: options), "ゑ", 36 | "we = ゑ (when useObsoleteKana is true)") 37 | 38 | XCTAssertEqual(WanaKana.toHiragana("only カナ"), "おんly かな", 39 | "pass romaji false by default") 40 | options = Options(passRomaji: true) 41 | XCTAssertEqual(WanaKana.toHiragana("only カナ", options: options), 42 | "only かな", "pass romaji") 43 | 44 | XCTAssertEqual(WanaKana.toHiragana("スーパー"), "すうぱあ", "converts to long vowels 1") 45 | XCTAssertEqual(WanaKana.toHiragana("バンゴー"), "ばんごう", "converts to long vowels 2") 46 | 47 | XCTAssertEqual(WanaKana.toHiragana("#22 22漢字、toukyou, オオサカ"), 48 | "#22 22漢字、とうきょう、 おおさか", 49 | "mixed input") 50 | } 51 | 52 | func testToKana() { 53 | XCTAssertEqual(WanaKana.toKana(""), "", "sanity check--empty string") 54 | 55 | XCTAssertEqual(WanaKana.toKana("onaji"), "おなじ", "lowercase transliterated to hiragana") 56 | XCTAssertEqual(WanaKana.toKana("buttsuuji"), "ぶっつうじ", "lowercase with double consonants and double vowels are transliterated to hiragana") 57 | XCTAssertEqual(WanaKana.toKana("ONAJI"), "オナジ", "uppercase transliterated to katakana") 58 | XCTAssertEqual(WanaKana.toKana("BUTTSUUJI"), "ブッツウジ", "uppercase with double consonants and double vowels are transliterated to hiragana") 59 | XCTAssertEqual(WanaKana.toKana("WaniKani"), "わにかに", "Mixed case returns hiragana (katakana only if all letters of mora are uppercased") 60 | XCTAssertEqual(WanaKana.toKana("ワニカニ AiUeO 鰐蟹 12345 @#$%"), "ワニカニ アいウえオ 鰐蟹 12345 @#$%", "Non-romaji will be passed through") 61 | XCTAssertEqual(WanaKana.toKana("座禅‘zazen’スタイル"), "座禅「ざぜん」スタイル", "handles mixed syllabaries") 62 | XCTAssertEqual(WanaKana.toKana("batsuge-mu"), "ばつげーむ", "will convert short to long dashes") 63 | 64 | XCTAssertEqual(WanaKana.toKana("n"), "ん", "w/out IME mode: solo n's are transliterated regardless of following chars - 1") 65 | XCTAssertEqual(WanaKana.toKana("shin"), "しん", "w/out IME mode: solo n's are transliterated regardless of following chars - 2") 66 | XCTAssertEqual(WanaKana.toKana("nn"), "んん", "w/out IME mode: double n's are transliterated to double ん") 67 | 68 | var options = Options(imeMode: .on) 69 | XCTAssertEqual(WanaKana.toKana("n", options: options), "n", "IME mode - 1") 70 | XCTAssertEqual(WanaKana.toKana("shin", options: options), "しn", "IME mode - 2") 71 | XCTAssertEqual(WanaKana.toKana("shinyou", options: options), "しにょう", "IME mode - 3") 72 | 73 | XCTAssertEqual(WanaKana.toKana("shin'you", options: options), "しんよう", "IME mode - 4") 74 | 75 | XCTAssertEqual(WanaKana.toKana("shin you", options: options), "しんよう", "IME mode - 5") 76 | XCTAssertEqual(WanaKana.toKana("nn", options: options), "ん", "IME mode: double n's are transliterated to single ん") 77 | 78 | XCTAssertEqual(WanaKana.toKana("wi"), "うぃ", "useObsoleteKana is false by default - 1") 79 | XCTAssertEqual(WanaKana.toKana("WI"), "ウィ", "useObsoleteKana is false by default - 1") 80 | options = Options(useObsoleteKana: true) 81 | XCTAssertEqual(WanaKana.toKana("wi", options: options), "ゐ", 82 | "wi = ゐ (when useObsoleteKana is true)") 83 | XCTAssertEqual(WanaKana.toKana("we", options: options), "ゑ", 84 | "we = ゑ (when useObsoleteKana is true)") 85 | XCTAssertEqual(WanaKana.toKana("WI", options: options), "ヰ", 86 | "WI = ヰ (when useObsoleteKana is true)") 87 | XCTAssertEqual(WanaKana.toKana("WE", options: options), "ヱ", 88 | "WE = ヱ (when useObsoleteKana is true)") 89 | } 90 | 91 | func testToKatakana() { 92 | XCTAssertEqual(WanaKana.toKatakana(""), "", "sanity check--empty string") 93 | 94 | var options = Options(useObsoleteKana: true) 95 | XCTAssertEqual(WanaKana.toKatakana("IROHANIHOHETO", options: options), "イロハニホヘト", 96 | "Iroha line 1") 97 | XCTAssertEqual(WanaKana.toKatakana("CHIRINURUWO", options: options), "チリヌルヲ", 98 | "Iroha line 2") 99 | XCTAssertEqual(WanaKana.toKatakana("WAKAYOTARESO", options: options), "ワカヨタレソ", 100 | "Iroha line 3") 101 | XCTAssertEqual(WanaKana.toKatakana("TSUNENARAMU", options: options), "ツネナラム", 102 | "Iroha line 4") 103 | XCTAssertEqual(WanaKana.toKatakana("UWINOOKUYAMA", options: options), "ウヰノオクヤマ", 104 | "Iroha line 5") 105 | XCTAssertEqual(WanaKana.toKatakana("KEFUKOETE", options: options), "ケフコエテ", 106 | "Iroha line 6") 107 | XCTAssertEqual(WanaKana.toKatakana("ASAKIYUMEMISHI", options: options), "アサキユメミシ", 108 | "Iroha line 7") 109 | XCTAssertEqual(WanaKana.toKatakana("WEHIMOSESU", options: options), "ヱヒモセス", 110 | "Iroha line 8") 111 | XCTAssertEqual(WanaKana.toKatakana("NLTU", options: options), "ンッ", 112 | "not in Iroha") 113 | 114 | XCTAssertEqual(WanaKana.toKatakana("wi"), "ウィ", "use obsolete false by default") 115 | options = Options(useObsoleteKana: false) 116 | XCTAssertEqual(WanaKana.toKatakana("wi", options: options), "ウィ", 117 | "wi = ウィ (when useObsoleteKana is false)") 118 | options = Options(useObsoleteKana: true) 119 | XCTAssertEqual(WanaKana.toKatakana("wi", options: options), "ヰ", 120 | "wi = ヰ (when useObsoleteKana is true)") 121 | XCTAssertEqual(WanaKana.toKatakana("we", options: options), "ヱ", 122 | "we = ヱ (when useObsoleteKana is true)") 123 | 124 | XCTAssertEqual(WanaKana.toKatakana("only カナ"), "オンly カナ", 125 | "pass romaji false by default") 126 | options = Options(passRomaji: true) 127 | XCTAssertEqual(WanaKana.toKatakana("only かな", options: options), 128 | "only カナ", "pass romaji") 129 | 130 | XCTAssertEqual(WanaKana.toKatakana("#22 22漢字、toukyou, オオサカ"), 131 | "#22 22漢字、トウキョウ、 オオサカ", 132 | "mixed input") 133 | } 134 | 135 | func testToRomaji() { 136 | XCTAssertEqual(WanaKana.toRomaji(""), "", "sanity check--empty string") 137 | 138 | XCTAssertEqual(WanaKana.toRomaji("ワニカニ ガ スゴイ ダ"), "wanikani ga sugoi da", 139 | "convert katakana to romaji") 140 | XCTAssertEqual(WanaKana.toRomaji("わにかに が すごい だ"), "wanikani ga sugoi da", 141 | "convert hiragana to romaji") 142 | XCTAssertEqual(WanaKana.toRomaji("ワニカニ が すごい だ"), "wanikani ga sugoi da", 143 | "convert mixed kana to romaji") 144 | 145 | let options = Options(upcaseKatakana: true) 146 | XCTAssertEqual(WanaKana.toRomaji("ワニカニ", options: options), "WANIKANI", 147 | "use the upcaseKatakana flag to preserve casing. Works for katakana") 148 | XCTAssertEqual(WanaKana.toRomaji("ワニカニ が すごい だ", options: options), 149 | "WANIKANI ga sugoi da", 150 | "use the upcaseKatakana flag to preserve casing. Works for mixed kana") 151 | 152 | XCTAssertEqual(WanaKana.toRomaji("ばつげーむ"), "batsuge-mu", "converts long dash 'ー' in hiragana to hyphen") 153 | XCTAssertEqual(WanaKana.toRomaji("一抹げーむ"), "一抹ge-mu", "doesn't confuse '一' (one kanji) for long dash 'ー'") 154 | XCTAssertEqual(WanaKana.toRomaji("スーパー"), "suupaa", "converts long dash 'ー' (chōonpu) in katakana to long vowel") 155 | XCTAssertEqual(WanaKana.toRomaji("缶コーヒー"), "缶koohii", "doesn't convert オー to 'ou' which occurs with hiragana") 156 | 157 | XCTAssertNotEqual(WanaKana.toRomaji("わにかにがすごいだ"), "wanikani ga sugoi da", "spaces must be manually entered") 158 | 159 | XCTAssertEqual(WanaKana.toRomaji("きんにくまん"), "kinnikuman", "double and single n") 160 | XCTAssertEqual(WanaKana.toRomaji("んんにんにんにゃんやん"), "nnninninnyan'yan", "N extravaganza") 161 | XCTAssertEqual(WanaKana.toRomaji("かっぱ たった しゅっしゅ ちゃっちゃ やっつ"), 162 | "kappa tatta shusshu chatcha yattsu", 163 | "double consonants") 164 | 165 | XCTAssertEqual(WanaKana.toRomaji("っ"), "", "small tsu doesn't transliterate") 166 | XCTAssertEqual(WanaKana.toRomaji("ヶ"), "ヶ", "small kata ke doesn't transliterate") 167 | XCTAssertEqual(WanaKana.toRomaji("ヵ"), "ヵ", "small kata ka doesn't transliterate") 168 | XCTAssertEqual(WanaKana.toRomaji("ゃ"), "ya", "small ya") 169 | XCTAssertEqual(WanaKana.toRomaji("ゅ"), "yu", "small yu") 170 | XCTAssertEqual(WanaKana.toRomaji("ょ"), "yo", "small yo") 171 | XCTAssertEqual(WanaKana.toRomaji("ぁ"), "a", "small a") 172 | XCTAssertEqual(WanaKana.toRomaji("ぃ"), "i", "small i") 173 | XCTAssertEqual(WanaKana.toRomaji("ぅ"), "u", "small u") 174 | XCTAssertEqual(WanaKana.toRomaji("ぇ"), "e", "small e") 175 | XCTAssertEqual(WanaKana.toRomaji("ぉ"), "o", "small o") 176 | 177 | XCTAssertEqual(WanaKana.toRomaji("おんよみ"), "on'yomi", "Apostrophes in ambiguous consonant vowel combos - 1") 178 | XCTAssertEqual(WanaKana.toRomaji("んよ んあ んゆ"), "n'yo n'a n'yu", "Apostrophes in ambiguous consonant vowel combos - 2") 179 | XCTAssertEqual(WanaKana.toRomaji("シンヨ"), "shin'yo", "Apostrophes in ambiguous consonant vowel combos - 3") 180 | } 181 | 182 | static var allTests = [ 183 | ("testToHiragana", testToHiragana), 184 | ("testToKana", testToKana), 185 | ("testToKatakana", testToKatakana), 186 | ("testToRomaji", testToRomaji), 187 | ] 188 | } 189 | 190 | -------------------------------------------------------------------------------- /Sources/WanaKana/Resources/wanakana.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.wanakana={})}(this,function(t){"use strict";var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=n(function(t){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)}),o=Object.freeze({default:r,__moduleExports:r}),i=n(function(t){var e=t.exports={version:"2.5.5"};"number"==typeof __e&&(__e=e)}),u=Object.freeze({default:i,__moduleExports:i,version:i.version}),a=function(t){return"object"==typeof t?null!==t:"function"==typeof t},c=Object.freeze({default:a,__moduleExports:a}),f=c&&a||c,s=function(t){if(!f(t))throw TypeError(t+" is not an object!");return t},l=Object.freeze({default:s,__moduleExports:s}),h=function(t){try{return!!t()}catch(t){return!0}},d=Object.freeze({default:h,__moduleExports:h}),v=d&&h||d,p=!v(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}),y=Object.freeze({default:p,__moduleExports:p}),g=o&&r||o,_=g.document,m=f(_)&&f(_.createElement),b=function(t){return m?_.createElement(t):{}},E=Object.freeze({default:b,__moduleExports:b}),O=y&&p||y,j=E&&b||E,w=!O&&!v(function(){return 7!=Object.defineProperty(j("div"),"a",{get:function(){return 7}}).a}),x=Object.freeze({default:w,__moduleExports:w}),S=function(t,e){if(!f(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!f(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!f(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!f(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")},A=Object.freeze({default:S,__moduleExports:S}),M=l&&s||l,z=x&&w||x,N=A&&S||A,P=Object.defineProperty,k=O?Object.defineProperty:function(t,e,n){if(M(t),e=N(e,!0),M(n),z)try{return P(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t},L={f:k},F=Object.freeze({default:L,__moduleExports:L,f:k}),I=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},R=Object.freeze({default:I,__moduleExports:I}),T=F&&L||F,C=R&&I||R,W=O?function(t,e,n){return T.f(t,e,C(1,n))}:function(t,e,n){return t[e]=n,t},U=Object.freeze({default:W,__moduleExports:W}),K={}.hasOwnProperty,B=function(t,e){return K.call(t,e)},D=Object.freeze({default:B,__moduleExports:B}),V=0,G=Math.random(),J=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++V+G).toString(36))},H=Object.freeze({default:J,__moduleExports:J}),Y=U&&W||U,q=D&&B||D,X=H&&J||H,$=u&&i||u,Q=n(function(t){var e=X("src"),n=Function.toString,r=(""+n).split("toString");$.inspectSource=function(t){return n.call(t)},(t.exports=function(t,n,o,i){var u="function"==typeof o;u&&(q(o,"name")||Y(o,"name",n)),t[n]!==o&&(u&&(q(o,e)||Y(o,e,t[n]?""+t[n]:r.join(n+""))),t===g?t[n]=o:i?t[n]?t[n]=o:Y(t,n,o):(delete t[n],Y(t,n,o)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[e]||n.call(this)})}),Z=Object.freeze({default:Q,__moduleExports:Q}),tt=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t},et=Object.freeze({default:tt,__moduleExports:tt}),nt=et&&tt||et,rt=function(t,e,n){if(nt(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}},ot=Object.freeze({default:rt,__moduleExports:rt}),it=Z&&Q||Z,ut=ot&&rt||ot,at=function(t,e,n){var r,o,i,u,a=t&at.F,c=t&at.G,f=t&at.P,s=t&at.B,l=c?g:t&at.S?g[e]||(g[e]={}):(g[e]||{}).prototype,h=c?$:$[e]||($[e]={}),d=h.prototype||(h.prototype={});for(r in c&&(n=e),n)i=((o=!a&&l&&void 0!==l[r])?l:n)[r],u=s&&o?ut(i,g):f&&"function"==typeof i?ut(Function.call,i):i,l&&it(l,r,i,t&at.U),h[r]!=i&&Y(h,r,u),f&&d[r]!=i&&(d[r]=i)};g.core=$,at.F=1,at.G=2,at.S=4,at.P=8,at.B=16,at.W=32,at.U=64,at.R=128;for(var ct,ft=at,st=Object.freeze({default:ft,__moduleExports:ft}),lt=X("typed_array"),ht=X("view"),dt=!(!g.ArrayBuffer||!g.DataView),vt=dt,pt=0,yt="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");9>pt;)(ct=g[yt[pt++]])?(Y(ct.prototype,lt,!0),Y(ct.prototype,ht,!0)):vt=!1;var gt={ABV:dt,CONSTR:vt,TYPED:lt,VIEW:ht},_t=Object.freeze({default:gt,__moduleExports:gt,ABV:gt.ABV,CONSTR:gt.CONSTR,TYPED:gt.TYPED,VIEW:gt.VIEW}),mt=Object.freeze({default:!1,__moduleExports:!1}),bt=function(t,e,n){for(var r in e)it(t,r,e[r],n);return t},Et=Object.freeze({default:bt,__moduleExports:bt}),Ot=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t},jt=Object.freeze({default:Ot,__moduleExports:Ot}),wt=Math.ceil,xt=Math.floor,St=function(t){return isNaN(t=+t)?0:(t>0?xt:wt)(t)},At=Object.freeze({default:St,__moduleExports:St}),Mt=At&&St||At,zt=Math.min,Nt=function(t){return t>0?zt(Mt(t),9007199254740991):0},Pt=Object.freeze({default:Nt,__moduleExports:Nt}),kt=Pt&&Nt||Pt,Lt=function(t){if(void 0===t)return 0;var e=Mt(t),n=kt(e);if(e!==n)throw RangeError("Wrong length!");return n},Ft=Object.freeze({default:Lt,__moduleExports:Lt}),It={}.toString,Rt=function(t){return It.call(t).slice(8,-1)},Tt=Object.freeze({default:Rt,__moduleExports:Rt}),Ct=Tt&&Rt||Tt,Wt=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==Ct(t)?t.split(""):Object(t)},Ut=Object.freeze({default:Wt,__moduleExports:Wt}),Kt=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t},Bt=Object.freeze({default:Kt,__moduleExports:Kt}),Dt=Ut&&Wt||Ut,Vt=Bt&&Kt||Bt,Gt=function(t){return Dt(Vt(t))},Jt=Object.freeze({default:Gt,__moduleExports:Gt}),Ht=Math.max,Yt=Math.min,qt=function(t,e){return 0>(t=Mt(t))?Ht(t+e,0):Yt(t,e)},Xt=Object.freeze({default:qt,__moduleExports:qt}),$t=Jt&&Gt||Jt,Qt=Xt&&qt||Xt,Zt=function(t){return function(e,n,r){var o,i=$t(e),u=kt(i.length),a=Qt(r,u);if(t&&n!=n){for(;u>a;)if((o=i[a++])!=o)return!0}else for(;u>a;a++)if((t||a in i)&&i[a]===n)return t||a||0;return!t&&-1}},te=Object.freeze({default:Zt,__moduleExports:Zt}),ee=g["__core-js_shared__"]||(g["__core-js_shared__"]={}),ne=function(t){return ee[t]||(ee[t]={})},re=Object.freeze({default:ne,__moduleExports:ne}),oe=re&&ne||re,ie=oe("keys"),ue=function(t){return ie[t]||(ie[t]=X(t))},ae=Object.freeze({default:ue,__moduleExports:ue}),ce=te&&Zt||te,fe=ae&&ue||ae,se=ce(!1),le=fe("IE_PROTO"),he=function(t,e){var n,r=$t(t),o=0,i=[];for(n in r)n!=le&&q(r,n)&&i.push(n);for(;e.length>o;)q(r,n=e[o++])&&(~se(i,n)||i.push(n));return i},de=Object.freeze({default:he,__moduleExports:he}),ve="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),pe=Object.freeze({default:ve,__moduleExports:ve}),ye=de&&he||de,ge=pe&&ve||pe,_e=ge.concat("length","prototype"),me=Object.getOwnPropertyNames||function(t){return ye(t,_e)},be={f:me},Ee=Object.freeze({default:be,__moduleExports:be,f:me}),Oe=function(t){return Object(Vt(t))},je=Object.freeze({default:Oe,__moduleExports:Oe}),we=je&&Oe||je,xe=function(t){for(var e=we(this),n=kt(e.length),r=arguments.length,o=Qt(r>1?arguments[1]:void 0,n),i=r>2?arguments[2]:void 0,u=void 0===i?n:Qt(i,n);u>o;)e[o++]=t;return e},Se=Object.freeze({default:xe,__moduleExports:xe}),Ae=n(function(t){var e=oe("wks"),n=g.Symbol,r="function"==typeof n;(t.exports=function(t){return e[t]||(e[t]=r&&n[t]||(r?n:X)("Symbol."+t))}).store=e}),Me=Object.freeze({default:Ae,__moduleExports:Ae}),ze=Me&&Ae||Me,Ne=T.f,Pe=ze("toStringTag"),ke=function(t,e,n){t&&!q(t=n?t:t.prototype,Pe)&&Ne(t,Pe,{configurable:!0,value:e})},Le=Object.freeze({default:ke,__moduleExports:ke}),Fe=mt,Ie=_t&>||_t,Re=Et&&bt||Et,Te=jt&&Ot||jt,Ce=Ft&&Lt||Ft,We=Ee&&be||Ee,Ue=Se&&xe||Se,Ke=Le&&ke||Le,Be=n(function(t,e){var n=We.f,r=T.f,o="prototype",i="Wrong index!",u=g.ArrayBuffer,a=g.DataView,c=g.Math,f=g.RangeError,s=g.Infinity,l=u,h=c.abs,d=c.pow,p=c.floor,y=c.log,_=c.LN2,m=O?"_b":"buffer",b=O?"_l":"byteLength",E=O?"_o":"byteOffset";function j(t,e,n){var r,o,i,u=Array(n),a=8*n-e-1,c=(1<>1,l=23===e?d(2,-24)-d(2,-77):0,v=0,g=0>t||0===t&&0>1/t?1:0;for((t=h(t))!=t||t===s?(o=t!=t?1:0,r=c):(r=p(y(t)/_),1>t*(i=d(2,-r))&&(r--,i*=2),2>(t+=1>r+f?l*d(2,1-f):l/i)*i||(r++,i/=2),c>r+f?1>r+f?(o=t*d(2,f-1)*d(2,e),r=0):(o=(t*i-1)*d(2,e),r+=f):(o=0,r=c));e>=8;u[v++]=255&o,o/=256,e-=8);for(r=r<0;u[v++]=255&r,r/=256,a-=8);return u[--v]|=128*g,u}function w(t,e,n){var r,o=8*n-e-1,i=(1<>1,a=o-7,c=n-1,f=t[c--],l=127&f;for(f>>=7;a>0;l=256*l+t[c],c--,a-=8);for(r=l&(1<<-a)-1,l>>=-a,a+=e;a>0;r=256*r+t[c],c--,a-=8);if(0===l)l=1-u;else{if(l===i)return r?NaN:f?-s:s;r+=d(2,e),l-=u}return(f?-1:1)*r*d(2,l-e)}function x(t){return t[3]<<24|t[2]<<16|t[1]<<8|t[0]}function S(t){return[255&t]}function A(t){return[255&t,t>>8&255]}function M(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]}function z(t){return j(t,52,8)}function N(t){return j(t,23,4)}function P(t,e,n){r(t[o],e,{get:function(){return this[n]}})}function k(t,e,n,r){var o=Ce(+n);if(o+e>t[b])throw f(i);var u=o+t[E],a=t[m]._b.slice(u,u+e);return r?a:a.reverse()}function L(t,e,n,r,o,u){var a=Ce(+n);if(a+e>t[b])throw f(i);for(var c=t[m]._b,s=a+t[E],l=r(+o),h=0;e>h;h++)c[s+h]=l[u?h:e-h-1]}if(Ie.ABV){if(!v(function(){u(1)})||!v(function(){new u(-1)})||v(function(){return new u,new u(1.5),new u(NaN),"ArrayBuffer"!=u.name})){for(var F,I=(u=function(t){return Te(this,u),new l(Ce(t))})[o]=l[o],R=n(l),C=0;R.length>C;)(F=R[C++])in u||Y(u,F,l[F]);Fe||(I.constructor=u)}var W=new a(new u(2)),U=a[o].setInt8;W.setInt8(0,2147483648),W.setInt8(1,2147483649),!W.getInt8(0)&&W.getInt8(1)||Re(a[o],{setInt8:function(t,e){U.call(this,t,e<<24>>24)},setUint8:function(t,e){U.call(this,t,e<<24>>24)}},!0)}else u=function(t){Te(this,u,"ArrayBuffer");var e=Ce(t);this._b=Ue.call(Array(e),0),this[b]=e},a=function(t,e,n){Te(this,a,"DataView"),Te(t,u,"DataView");var r=t[b],o=Mt(e);if(0>o||o>r)throw f("Wrong offset!");if(o+(n=void 0===n?r-o:kt(n))>r)throw f("Wrong length!");this[m]=t,this[E]=o,this[b]=n},O&&(P(u,"byteLength","_l"),P(a,"buffer","_b"),P(a,"byteLength","_l"),P(a,"byteOffset","_o")),Re(a[o],{getInt8:function(t){return k(this,1,t)[0]<<24>>24},getUint8:function(t){return k(this,1,t)[0]},getInt16:function(t){var e=k(this,2,t,arguments[1]);return(e[1]<<8|e[0])<<16>>16},getUint16:function(t){var e=k(this,2,t,arguments[1]);return e[1]<<8|e[0]},getInt32:function(t){return x(k(this,4,t,arguments[1]))},getUint32:function(t){return x(k(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return w(k(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return w(k(this,8,t,arguments[1]),52,8)},setInt8:function(t,e){L(this,1,t,S,e)},setUint8:function(t,e){L(this,1,t,S,e)},setInt16:function(t,e){L(this,2,t,A,e,arguments[2])},setUint16:function(t,e){L(this,2,t,A,e,arguments[2])},setInt32:function(t,e){L(this,4,t,M,e,arguments[2])},setUint32:function(t,e){L(this,4,t,M,e,arguments[2])},setFloat32:function(t,e){L(this,4,t,N,e,arguments[2])},setFloat64:function(t,e){L(this,8,t,z,e,arguments[2])}});Ke(u,"ArrayBuffer"),Ke(a,"DataView"),Y(a[o],Ie.VIEW,!0),e.ArrayBuffer=u,e.DataView=a}),De=Object.freeze({default:Be,__moduleExports:Be}),Ve=ze("species"),Ge=function(t,e){var n,r=M(t).constructor;return void 0===r||void 0==(n=M(r)[Ve])?e:nt(n)},Je=Object.freeze({default:Ge,__moduleExports:Ge}),He=ze("species"),Ye=function(t){var e=g[t];O&&e&&!e[He]&&T.f(e,He,{configurable:!0,get:function(){return this}})},qe=Object.freeze({default:Ye,__moduleExports:Ye}),Xe=st&&ft||st,$e=De&&Be||De,Qe=Je&&Ge||Je,Ze=qe&&Ye||qe,tn=g.ArrayBuffer,en=$e.ArrayBuffer,nn=$e.DataView,rn=Ie.ABV&&tn.isView,on=en.prototype.slice,un=Ie.VIEW;Xe(Xe.G+Xe.W+Xe.F*(tn!==en),{ArrayBuffer:en}),Xe(Xe.S+Xe.F*!Ie.CONSTR,"ArrayBuffer",{isView:function(t){return rn&&rn(t)||f(t)&&un in t}}),Xe(Xe.P+Xe.U+Xe.F*v(function(){return!new en(2).slice(1,void 0).byteLength}),"ArrayBuffer",{slice:function(t,e){if(void 0!==on&&void 0===e)return on.call(M(this),t);for(var n=M(this).byteLength,r=Qt(t,n),o=Qt(void 0===e?n:e,n),i=new(Qe(this,en))(kt(o-r)),u=new nn(this),a=new nn(i),c=0;o>r;)a.setUint8(c++,u.getUint8(r++));return i}}),Ze("ArrayBuffer");var an=ze("toStringTag"),cn="Arguments"==Ct(function(){return arguments}()),fn=function(t){var e,n,r;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),an))?n:cn?Ct(e):"Object"==(r=Ct(e))&&"function"==typeof e.callee?"Arguments":r},sn=Object.freeze({default:fn,__moduleExports:fn}),ln={},hn=Object.freeze({default:ln,__moduleExports:ln}),dn=hn&&ln||hn,vn=ze("iterator"),pn=Array.prototype,yn=function(t){return void 0!==t&&(dn.Array===t||pn[vn]===t)},gn=Object.freeze({default:yn,__moduleExports:yn}),_n=Object.keys||function(t){return ye(t,ge)},mn=Object.freeze({default:_n,__moduleExports:_n}),bn=mn&&_n||mn,En=O?Object.defineProperties:function(t,e){M(t);for(var n,r=bn(e),o=r.length,i=0;o>i;)T.f(t,n=r[i++],e[n]);return t},On=Object.freeze({default:En,__moduleExports:En}),jn=g.document,wn=jn&&jn.documentElement,xn=Object.freeze({default:wn,__moduleExports:wn}),Sn=On&&En||On,An=xn&&wn||xn,Mn=fe("IE_PROTO"),zn=function(){},Nn=function(){var t,e=j("iframe"),n=ge.length;for(e.style.display="none",An.appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write("